From 4c9b760d098283d89472cca1e58fa7d8e49eeae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Sun, 5 Dec 2021 17:31:09 +0100 Subject: [PATCH 1/5] update readme to run tests change composer.json test memcache to use 127.0.0.1 instead of localhost --- README.md | 69 ++++++++------------------------------------------- composer.json | 2 +- test.sh | 0 3 files changed, 11 insertions(+), 60 deletions(-) mode change 100644 => 100755 test.sh diff --git a/README.md b/README.md index 2f7ddcc..5c56293 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,12 @@ -

+# Comment lancer les tests unitaires ? -

-Build Status -Total Downloads -Latest Stable Version -License -

+Prérequis : + - Avoir un serveur memcached sur 127.0.0.1:11211 + - Avoir l'extension memcached -## 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 : +- composer install +- service start memcached +- service start mysql (votre base de donnée accessible sur 127.0.0.1:3306) +- chmod u+x ./test.sh +- composer run test diff --git a/composer.json b/composer.json index ab86fb2..fa124ee 100644 --- a/composer.json +++ b/composer.json @@ -87,7 +87,7 @@ "prefer-stable": true, "scripts": { "test": [ - "echo 'check memcached is running';netcat -z localhost 11211", + "echo 'check memcached is running';netcat -z 127.0.0.1 11211", "@configclear", "./test.sh" ], diff --git a/test.sh b/test.sh old mode 100644 new mode 100755 From e1b2d86315418485247e3fe3484f6dc8226a7f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Sun, 5 Dec 2021 18:28:53 +0100 Subject: [PATCH 2/5] cleanup git module flaw --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 05894ad..13a6489 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 From d7487586760b00c9fa2faee239fe58fee22b7dca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Sun, 5 Dec 2021 19:28:53 +0100 Subject: [PATCH 3/5] update readme tests --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 5c56293..fbd3cd8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,18 @@ # Comment lancer les tests unitaires ? +### 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 + Prérequis : - Avoir un serveur memcached sur 127.0.0.1:11211 - Avoir l'extension memcached 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 memcached - service start mysql (votre base de donnée accessible sur 127.0.0.1:3306) From c94827127b916fa8e14317da41f7b5225fe8e9a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Sun, 5 Dec 2021 19:29:10 +0100 Subject: [PATCH 4/5] fix arcadia testValidateGeneratedXmlGame --- tests/Unit/CrmClientV2ArcadiaTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/CrmClientV2ArcadiaTest.php b/tests/Unit/CrmClientV2ArcadiaTest.php index a60d413..4cb34f9 100644 --- a/tests/Unit/CrmClientV2ArcadiaTest.php +++ b/tests/Unit/CrmClientV2ArcadiaTest.php @@ -69,7 +69,7 @@ class CrmClientV2ArcadiaTest extends CrmClientTester /** @var \Symfony\Component\Finder\SplFileInfo $file */ foreach ($files as $file) { - if (\strpos($file->getFilename(), 'v2-game') === 0) { + if (\strpos($file->getFilename(), 'v2-order-000000000-game') === 0) { $fileFound = true; // Enable user error handling From 81aabdab7279fde247811ecf6114609ed4649b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Sun, 19 Dec 2021 15:57:24 +0100 Subject: [PATCH 5/5] Version 3.0.0 This removes api authentification with token in favor of signature method. Refactorize code and clean up. It add more unit test plus code coverage in test/converage. - Added config.api.timestampTimeout - Added Helper::signRequest This calculates the request signature - Added more unit tests and extend code coverage. - Added rate limiter for web routes - Added RemoveSpaces Sanitizer on api requests - Added stress test suite - Changed AuthServiceProvider api-token to api-hmac - Changed CustomerSeeder to have at least of customer activated (for unit tests) - Changed used transation for saveing multiple models - Changed, in ServiceController generateApiToken to generateServicePassword - Changed, timestamp has to be send in request with a DateTime format, it also has to a UTC value - Composer added brianium/paratest - Composer update - Fixed case where removing single optin could be the wrong one (not for the good service) - Fixed customer delete - Fixed customer would not be resolved by brand. - Fixed Really check that request is json checking body content, preventing sending malformed requests - Remove ApiError::needAuth - Removed api_token - Removed ApiController::authenticate - Removed config.api.tokenTimeout - Removed Header check or usage because of HAProxy doing weird stuff - Removed lock files from gitignore - Removed memcached dependency - Removed unused questions resources api routes - Removed workspace file Migrations - Added unique index on services.bearer_token - Changed customer_id on customer_answer is nullable - Changed services.bearer_pwd length to 312 (the size of the token) - Dropped services.api_token and service.api_token_date --- .gitignore | 2 - CRM V2.code-workspace | 7 - README.md | 6 +- .../Commands/DeleteUnconfirmedCustomers.php | 17 +- app/Console/Commands/GenerateCrmClient.php | 43 +- app/Console/Commands/LdcWebStatusChecker.php | 32 +- app/Console/Commands/MigrateCrmUsers.php | 8 +- app/Console/Commands/StartStatJobs.php | 6 +- app/Console/Commands/UserCreate.php | 9 +- app/Console/Kernel.php | 7 +- app/Enums/ApiError.php | 17 +- app/Enums/ArcadiaMailType.php | 5 + app/Enums/ArcadiaOrderType.php | 5 + app/Enums/Country.php | 10 + app/Enums/CustomerExportType.php | 14 +- app/Enums/QuestionType.php | 5 + app/Exceptions/Api/V2/ApiErrorException.php | 21 +- app/Exceptions/Api/V2/CrmException.php | 7 + app/Exceptions/Handler.php | 175 +- app/Http/Controllers/AnswerController.php | 20 + .../Controllers/Api/V1/ArcadiaController.php | 62 +- .../Controllers/Api/V1/CustomerController.php | 114 +- .../Controllers/Api/V2/AnswerController.php | 13 +- app/Http/Controllers/Api/V2/ApiController.php | 36 +- .../Controllers/Api/V2/ArcadiaController.php | 90 +- .../Controllers/Api/V2/CustomerController.php | 278 +- .../Controllers/Api/V2/OptinController.php | 27 +- .../Controllers/Api/V2/QuestionController.php | 28 +- .../Controllers/Api/V2/StatsController.php | 28 +- app/Http/Controllers/BrandController.php | 14 + app/Http/Controllers/Controller.php | 3 + app/Http/Controllers/CustomerController.php | 116 +- app/Http/Controllers/LoginController.php | 20 +- app/Http/Controllers/QuestionController.php | 64 +- app/Http/Controllers/ServiceController.php | 40 +- .../Controllers/SingleOptinController.php | 36 +- app/Http/Controllers/StatsController.php | 37 +- app/Http/Kernel.php | 1 - app/Http/Middleware/ApiCheckTimeStamps.php | 23 +- app/Http/Middleware/ApiResponse.php | 63 +- app/Http/Middleware/Authenticate.php | 22 +- app/Http/Middleware/CheckBrand.php | 13 +- app/Http/Middleware/EncryptCookies.php | 5 +- app/Http/Middleware/JsonOnly.php | 18 +- .../PreventRequestsDuringMaintenance.php | 5 +- .../Middleware/RedirectIfAuthenticated.php | 9 +- app/Http/Middleware/TrimStrings.php | 1 - app/Http/Middleware/TrustHosts.php | 3 + app/Http/Middleware/TrustProxies.php | 3 +- app/Http/Middleware/VerifyCsrfToken.php | 5 +- app/Http/Middleware/XssSanitizer.php | 4 +- .../Requests/Api/V2/AuthenticateRequest.php | 2 +- app/Http/Requests/Api/V2/BaseApiRequest.php | 11 +- .../Requests/Api/V2/GetBrandStatsRequest.php | 6 +- .../Api/V2/GetQuestionStatsRequest.php | 5 +- .../Api/V2/SendArchadiaDefaultMailRequest.php | 14 + .../Api/V2/SendArchadiaGameMailRequest.php | 14 + .../Requests/Api/V2/SingleOptinRequest.php | 5 + .../Requests/Api/V2/StoreCustomerAnswers.php | 2 +- .../Requests/Api/V2/StoreCustomerRequest.php | 7 +- .../Api/V2/StoreSingleOptinRequest.php | 13 - .../Requests/Api/V2/UpdateCustomerRequest.php | 44 +- .../Api/V2/UpdateSingleOptinRequest.php | 14 - app/Http/Requests/CustomerExport.php | 5 +- ...eApiToken.php => GenerateServiceToken.php} | 11 +- app/Http/Requests/ManageBrandRequest.php | 9 +- app/Http/Requests/ManageServiceRequest.php | 9 +- app/Http/Requests/QuestionExport.php | 5 +- app/Http/Requests/StatsByBrandRequest.php | 9 +- app/Http/Requests/StatsByQuestionRequest.php | 9 +- app/Http/Resources/Api/V2/AnswersResource.php | 4 +- app/Http/Resources/Api/V2/ApiResponse.php | 8 +- .../Resources/Api/V2/BrandStatsResource.php | 4 +- .../Resources/Api/V2/CustomerResource.php | 21 +- .../Resources/Api/V2/QuestionResource.php | 34 - .../Api/V2/QuestionStatsResource.php | 11 +- .../Resources/Api/V2/QuestionsResource.php | 8 +- .../Resources/Api/V2/SingleOptinResource.php | 21 - app/Http/Resources/CustomerResource.php | 15 +- app/Http/Resources/StatusResource.php | 3 + app/Http/Sanitizers/RemoveSpaces.php | 17 + app/Jobs/SetServiceStats.php | 14 + app/Jobs/StartStatJobs.php | 8 + app/Jobs/StatJob.php | 12 +- app/Jobs/UpdateCustomerLastConnection.php | 6 + app/Lib/Helper.php | 107 +- app/Lib/MigrateOldData.php | 36 +- app/Mail/CourrierArcadiaError.php | 11 +- app/Mail/ServiceCredentialRenewed.php | 6 +- app/Models/Answer.php | 14 + app/Models/Brand.php | 48 +- app/Models/BrandStat.php | 28 +- app/Models/Customer.php | 215 +- app/Models/CustomerAnswer.php | 33 +- app/Models/CustomerInfo.php | 80 +- app/Models/CustomerLogin.php | 37 +- app/Models/CustomerOptInHistory.php | 7 + app/Models/Question.php | 19 + app/Models/Service.php | 272 +- app/Models/ServiceStat.php | 12 + app/Models/SingleOptin.php | 14 + app/Policies/AnswerPolicy.php | 3 + app/Policies/BrandPolicy.php | 3 + app/Policies/CustomerPolicy.php | 10 + app/Policies/QuestionPolicy.php | 10 + app/Policies/Rules/UserStaticRules.php | 13 +- app/Policies/ServicePolicy.php | 3 + app/Policies/SingleOptinPolicy.php | 3 + app/Providers/AppServiceProvider.php | 25 +- app/Providers/AuthServiceProvider.php | 54 +- app/Providers/BroadcastServiceProvider.php | 21 - app/Providers/EventServiceProvider.php | 14 +- app/Providers/RouteServiceProvider.php | 59 +- clients/V2 | 2 +- composer.json | 215 +- composer.lock | 1260 +- config/api.php | 6 +- config/app.php | 178 +- config/auth.php | 114 +- config/broadcasting.php | 36 +- config/cache.php | 54 +- config/cors.php | 20 +- config/database.php | 74 +- config/filesystems.php | 68 +- config/hashing.php | 52 +- config/logging.php | 40 +- config/mail.php | 78 +- config/queue.php | 52 +- config/services.php | 18 +- config/session.php | 238 +- config/trustedproxy.php | 11 +- config/view.php | 32 +- database/factories/AnswerFactory.php | 1 - database/factories/BrandFactory.php | 1 - database/factories/CustomerFactory.php | 1 - database/factories/CustomerInfoFactory.php | 33 +- database/factories/CustomerLoginFactory.php | 8 +- database/factories/QuestionFactory.php | 1 - database/factories/ServiceFactory.php | 1 - database/factories/UserFactory.php | 4 +- ...07_203437_drop_api_token_from_services.php | 34 + ...que_constrain_on_api_token_on_services.php | 32 + ...000551_increase_bearer_pwd_on_services.php | 31 + ...152449_parse_country_on_customer_infos.php | 65 + ...ustomer_id_nullable_on_customer_answer.php | 42 + database/seeders/BrandSeeder.php | 2 +- database/seeders/BrandStatSeeder.php | 15 +- database/seeders/CustomerSeeder.php | 9 +- package-lock.json | 17645 ++++++++++++++++ phpcs.xml | 157 +- phpunit.xml | 27 +- routes/api.php | 54 +- routes/channels.php | 18 - routes/console.php | 16 +- routes/web.php | 30 +- test.sh | 13 +- tests/{Feature => CRM}/ApiV2/ApiBase.php | 71 +- tests/CRM/ApiV2/ApiTest.php | 186 + tests/{Feature => CRM}/ApiV2/ArcadiaTest.php | 11 +- tests/CRM/ApiV2/CustomerTest.php | 710 + tests/CRM/ApiV2/QuestionTest.php | 303 + .../ApiV2/SingleOptinTest.php | 60 +- tests/CRM/ApiV2/StatsTest.php | 202 + tests/{Unit => Client}/CrmClientTester.php | 57 +- .../CrmClientV1CustomerTest.php | 203 +- tests/Client/CrmClientV2ArcadiaTest.php | 266 + .../CrmClientV2ClientTest.php | 27 +- .../CrmClientV2ConfigTest.php | 7 +- .../CrmClientV2CustomerTest.php | 308 +- tests/Client/CrmClientV2QuestionTest.php | 263 + .../CrmClientV2RouterTest.php | 23 +- tests/Client/CrmClientV2SingleOptinTest.php | 64 + .../{Unit => Client}/CrmClientV2StatsTest.php | 40 +- tests/{Unit => Client}/HelperTest.php | 35 +- tests/Client/UtilsTest.php | 79 + tests/Feature/ApiV2/ApiTest.php | 60 - tests/Feature/ApiV2/CustomerTest.php | 295 - tests/Feature/ApiV2/QuestionTest.php | 72 - tests/Feature/ApiV2/StatsTest.php | 89 - tests/StressTest/RunMultipleRequestsTest.php | 128 + tests/Unit/CrmClientV2ArcadiaTest.php | 122 - tests/Unit/CrmClientV2QuestionTest.php | 75 - tests/Unit/CrmClientV2SingleOptinTest.php | 39 - .../DeleteUnconfirmedCustomers.php.html | 168 + .../Commands/GenerateCrmClient.php.html | 368 + .../Commands/LdcWebStatusChecker.php.html | 191 + .../Console/Commands/MigrateCrmUsers.php.html | 186 + .../Console/Commands/StartStatJobs.php.html | 150 + .../Console/Commands/UserCreate.php.html | 163 + .../coverage/Console/Commands/dashboard.html | 285 + tests/coverage/Console/Commands/index.html | 155 + tests/coverage/Console/Kernel.php.html | 166 + tests/coverage/Console/dashboard.html | 284 + tests/coverage/Console/index.html | 102 + tests/coverage/Enums/ApiError.php.html | 287 + tests/coverage/Enums/ApiStatus.php.html | 117 + tests/coverage/Enums/ArcadiaMailType.php.html | 180 + .../coverage/Enums/ArcadiaOrderType.php.html | 180 + tests/coverage/Enums/Country.php.html | 957 + .../Enums/CustomerExportType.php.html | 166 + tests/coverage/Enums/QuestionType.php.html | 182 + tests/coverage/Enums/dashboard.html | 284 + tests/coverage/Enums/index.html | 257 + .../AbortRowImportException.php.html | 115 + .../Api/V2/ApiErrorException.php.html | 321 + .../Exceptions/Api/V2/CrmException.php.html | 143 + .../coverage/Exceptions/Api/V2/dashboard.html | 290 + tests/coverage/Exceptions/Api/V2/index.html | 134 + tests/coverage/Exceptions/Api/dashboard.html | 289 + tests/coverage/Exceptions/Api/index.html | 120 + tests/coverage/Exceptions/Handler.php.html | 386 + tests/coverage/Exceptions/dashboard.html | 290 + tests/coverage/Exceptions/index.html | 160 + .../Controllers/AnswerController.php.html | 186 + .../Api/V1/ArcadiaController.php.html | 511 + .../Api/V1/CustomerController.php.html | 784 + .../Http/Controllers/Api/V1/dashboard.html | 287 + .../Http/Controllers/Api/V1/index.html | 105 + .../Api/V2/AnswerController.php.html | 190 + .../Controllers/Api/V2/ApiController.php.html | 174 + .../Api/V2/ArcadiaController.php.html | 357 + .../Api/V2/CustomerController.php.html | 512 + .../Api/V2/OptinController.php.html | 208 + .../Api/V2/QuestionController.php.html | 261 + .../Api/V2/StatsController.php.html | 306 + .../Http/Controllers/Api/V2/dashboard.html | 287 + .../Http/Controllers/Api/V2/index.html | 290 + .../Http/Controllers/Api/dashboard.html | 286 + .../coverage/Http/Controllers/Api/index.html | 134 + .../Http/Controllers/BrandController.php.html | 157 + .../Http/Controllers/Controller.php.html | 120 + .../Controllers/CustomerController.php.html | 482 + .../Http/Controllers/LoginController.php.html | 189 + .../Controllers/QuestionController.php.html | 305 + .../Controllers/ServiceController.php.html | 196 + .../SingleOptinController.php.html | 182 + .../Http/Controllers/StatsController.php.html | 355 + .../coverage/Http/Controllers/dashboard.html | 285 + tests/coverage/Http/Controllers/index.html | 237 + tests/coverage/Http/Kernel.php.html | 170 + .../Middleware/ApiCheckTimeStamps.php.html | 194 + .../Http/Middleware/ApiResponse.php.html | 226 + .../Http/Middleware/Authenticate.php.html | 222 + .../Http/Middleware/CheckBrand.php.html | 153 + .../Http/Middleware/EncryptCookies.php.html | 117 + .../Http/Middleware/JsonOnly.php.html | 184 + .../PreventRequestsDuringMaintenance.php.html | 117 + .../RedirectIfAuthenticated.php.html | 148 + .../Http/Middleware/TrimStrings.php.html | 120 + .../Http/Middleware/TrustHosts.php.html | 136 + .../Http/Middleware/TrustProxies.php.html | 129 + .../Http/Middleware/VerifyCsrfToken.php.html | 117 + .../Http/Middleware/XssSanitizer.php.html | 178 + tests/coverage/Http/Middleware/dashboard.html | 285 + tests/coverage/Http/Middleware/index.html | 336 + .../Api/V2/AuthenticateRequest.php.html | 205 + .../Requests/Api/V2/BaseApiRequest.php.html | 216 + .../Api/V2/GetBrandStatsRequest.php.html | 194 + .../Api/V2/GetQuestionStatsRequest.php.html | 195 + .../SendArchadiaDefaultMailRequest.php.html | 304 + .../V2/SendArchadiaGameMailRequest.php.html | 297 + .../Api/V2/SingleOptinRequest.php.html | 174 + .../Api/V2/StoreCustomerAnswers.php.html | 202 + .../Api/V2/StoreCustomerRequest.php.html | 257 + .../Api/V2/UpdateCustomerRequest.php.html | 266 + .../Http/Requests/Api/V2/dashboard.html | 295 + .../coverage/Http/Requests/Api/V2/index.html | 374 + .../coverage/Http/Requests/Api/dashboard.html | 294 + tests/coverage/Http/Requests/Api/index.html | 121 + .../Http/Requests/CustomerExport.php.html | 168 + .../Requests/GenerateServiceToken.php.html | 155 + .../Http/Requests/ManageBrandRequest.php.html | 176 + .../Requests/ManageServiceRequest.php.html | 177 + .../Http/Requests/QuestionExport.php.html | 157 + .../Requests/StatsByBrandRequest.php.html | 181 + .../Requests/StatsByQuestionRequest.php.html | 181 + tests/coverage/Http/Requests/dashboard.html | 293 + tests/coverage/Http/Requests/index.html | 211 + .../Resources/Api/V2/AnswersResource.php.html | 181 + .../Resources/Api/V2/ApiResponse.php.html | 218 + .../Api/V2/BrandStatsResource.php.html | 188 + .../Api/V2/CustomerResource.php.html | 297 + .../Api/V2/QuestionStatsResource.php.html | 217 + .../Api/V2/QuestionsResource.php.html | 230 + .../Http/Resources/Api/V2/dashboard.html | 287 + .../coverage/Http/Resources/Api/V2/index.html | 262 + .../Http/Resources/Api/dashboard.html | 286 + tests/coverage/Http/Resources/Api/index.html | 121 + .../Http/Resources/CustomerResource.php.html | 180 + .../Http/Resources/StatusResource.php.html | 114 + tests/coverage/Http/Resources/dashboard.html | 285 + tests/coverage/Http/Resources/index.html | 146 + .../Http/Sanitizers/RemoveSpaces.php.html | 170 + tests/coverage/Http/Sanitizers/dashboard.html | 285 + tests/coverage/Http/Sanitizers/index.html | 120 + tests/coverage/Http/dashboard.html | 292 + tests/coverage/Http/index.html | 244 + tests/coverage/Jobs/SetServiceStats.php.html | 227 + tests/coverage/Jobs/StartStatJobs.php.html | 145 + tests/coverage/Jobs/StatJob.php.html | 220 + .../UpdateCustomerLastConnection.php.html | 186 + tests/coverage/Jobs/dashboard.html | 284 + tests/coverage/Jobs/index.html | 128 + tests/coverage/Lib/Helper.php.html | 493 + tests/coverage/Lib/MigrateOldData.php.html | 389 + tests/coverage/Lib/dashboard.html | 290 + tests/coverage/Lib/index.html | 132 + .../Mail/CourrierArcadiaError.php.html | 187 + .../Mail/ServiceCredentialRenewed.php.html | 182 + tests/coverage/Mail/dashboard.html | 284 + tests/coverage/Mail/index.html | 102 + tests/coverage/Models/Answer.php.html | 273 + tests/coverage/Models/Brand.php.html | 519 + tests/coverage/Models/BrandStat.php.html | 235 + tests/coverage/Models/Customer.php.html | 553 + tests/coverage/Models/CustomerAnswer.php.html | 235 + tests/coverage/Models/CustomerInfo.php.html | 356 + tests/coverage/Models/CustomerLogin.php.html | 307 + .../Models/CustomerOptInHistory.php.html | 213 + tests/coverage/Models/Question.php.html | 316 + tests/coverage/Models/Service.php.html | 607 + tests/coverage/Models/ServiceStat.php.html | 222 + tests/coverage/Models/SingleOptin.php.html | 273 + tests/coverage/Models/User.php.html | 144 + tests/coverage/Models/dashboard.html | 309 + tests/coverage/Models/index.html | 440 + tests/coverage/Policies/AnswerPolicy.php.html | 145 + tests/coverage/Policies/BrandPolicy.php.html | 145 + .../coverage/Policies/CustomerPolicy.php.html | 173 + .../coverage/Policies/QuestionPolicy.php.html | 170 + .../Policies/Rules/UserStaticRules.php.html | 156 + tests/coverage/Policies/Rules/dashboard.html | 285 + tests/coverage/Policies/Rules/index.html | 90 + .../coverage/Policies/ServicePolicy.php.html | 145 + .../Policies/SingleOptinPolicy.php.html | 145 + tests/coverage/Policies/dashboard.html | 284 + tests/coverage/Policies/index.html | 167 + .../Providers/AppServiceProvider.php.html | 184 + .../Providers/AuthServiceProvider.php.html | 228 + .../Providers/EventServiceProvider.php.html | 125 + .../Providers/RouteServiceProvider.php.html | 280 + tests/coverage/Providers/dashboard.html | 284 + tests/coverage/Providers/index.html | 173 + tests/coverage/_css/bootstrap.min.css | 7 + tests/coverage/_css/custom.css | 0 tests/coverage/_css/nv.d3.min.css | 1 + tests/coverage/_css/octicons.css | 5 + tests/coverage/_css/style.css | 127 + tests/coverage/_icons/file-code.svg | 1 + tests/coverage/_icons/file-directory.svg | 1 + tests/coverage/_js/bootstrap.min.js | 7 + tests/coverage/_js/d3.min.js | 5 + tests/coverage/_js/file.js | 62 + tests/coverage/_js/jquery.min.js | 2 + tests/coverage/_js/nv.d3.min.js | 8 + tests/coverage/_js/popper.min.js | 5 + .../DeleteUnconfirmedCustomers.php.html | 169 + .../Commands/GenerateCrmClient.php.html | 369 + .../Commands/LdcWebStatusChecker.php.html | 192 + .../Console/Commands/MigrateCrmUsers.php.html | 187 + .../Console/Commands/StartStatJobs.php.html | 151 + .../app/Console/Commands/UserCreate.php.html | 164 + .../app/Console/Commands/dashboard.html | 286 + .../coverage/app/Console/Commands/index.html | 156 + tests/coverage/app/Console/Kernel.php.html | 167 + tests/coverage/app/Console/dashboard.html | 285 + tests/coverage/app/Console/index.html | 103 + tests/coverage/app/Enums/ApiError.php.html | 288 + tests/coverage/app/Enums/ApiStatus.php.html | 118 + .../app/Enums/ArcadiaMailType.php.html | 181 + .../app/Enums/ArcadiaOrderType.php.html | 181 + tests/coverage/app/Enums/Country.php.html | 958 + .../app/Enums/CustomerExportType.php.html | 167 + .../coverage/app/Enums/QuestionType.php.html | 183 + tests/coverage/app/Enums/dashboard.html | 285 + tests/coverage/app/Enums/index.html | 258 + .../AbortRowImportException.php.html | 116 + .../Api/V2/ApiErrorException.php.html | 322 + .../Exceptions/Api/V2/CrmException.php.html | 144 + .../app/Exceptions/Api/V2/dashboard.html | 291 + .../coverage/app/Exceptions/Api/V2/index.html | 135 + .../app/Exceptions/Api/dashboard.html | 290 + tests/coverage/app/Exceptions/Api/index.html | 121 + .../coverage/app/Exceptions/Handler.php.html | 387 + tests/coverage/app/Exceptions/dashboard.html | 291 + tests/coverage/app/Exceptions/index.html | 161 + .../Controllers/AnswerController.php.html | 187 + .../Api/V1/ArcadiaController.php.html | 512 + .../Api/V1/CustomerController.php.html | 785 + .../Http/Controllers/Api/V1/dashboard.html | 288 + .../app/Http/Controllers/Api/V1/index.html | 106 + .../Api/V2/AnswerController.php.html | 191 + .../Controllers/Api/V2/ApiController.php.html | 175 + .../Api/V2/ArcadiaController.php.html | 358 + .../Api/V2/CustomerController.php.html | 513 + .../Api/V2/OptinController.php.html | 209 + .../Api/V2/QuestionController.php.html | 262 + .../Api/V2/StatsController.php.html | 307 + .../Http/Controllers/Api/V2/dashboard.html | 288 + .../app/Http/Controllers/Api/V2/index.html | 291 + .../app/Http/Controllers/Api/dashboard.html | 287 + .../app/Http/Controllers/Api/index.html | 135 + .../Http/Controllers/BrandController.php.html | 158 + .../app/Http/Controllers/Controller.php.html | 121 + .../Controllers/CustomerController.php.html | 483 + .../Http/Controllers/LoginController.php.html | 190 + .../Controllers/QuestionController.php.html | 306 + .../Controllers/ServiceController.php.html | 197 + .../SingleOptinController.php.html | 183 + .../Http/Controllers/StatsController.php.html | 356 + .../app/Http/Controllers/dashboard.html | 286 + .../coverage/app/Http/Controllers/index.html | 238 + tests/coverage/app/Http/Kernel.php.html | 171 + .../Middleware/ApiCheckTimeStamps.php.html | 195 + .../app/Http/Middleware/ApiResponse.php.html | 227 + .../app/Http/Middleware/Authenticate.php.html | 223 + .../app/Http/Middleware/CheckBrand.php.html | 154 + .../Http/Middleware/EncryptCookies.php.html | 118 + .../app/Http/Middleware/JsonOnly.php.html | 185 + .../PreventRequestsDuringMaintenance.php.html | 118 + .../RedirectIfAuthenticated.php.html | 149 + .../app/Http/Middleware/TrimStrings.php.html | 121 + .../app/Http/Middleware/TrustHosts.php.html | 137 + .../app/Http/Middleware/TrustProxies.php.html | 130 + .../Http/Middleware/VerifyCsrfToken.php.html | 118 + .../app/Http/Middleware/XssSanitizer.php.html | 179 + .../app/Http/Middleware/dashboard.html | 286 + tests/coverage/app/Http/Middleware/index.html | 337 + .../Api/V2/AuthenticateRequest.php.html | 206 + .../Requests/Api/V2/BaseApiRequest.php.html | 216 + .../Api/V2/GetBrandStatsRequest.php.html | 195 + .../Api/V2/GetQuestionStatsRequest.php.html | 196 + .../SendArchadiaDefaultMailRequest.php.html | 305 + .../V2/SendArchadiaGameMailRequest.php.html | 298 + .../Api/V2/SingleOptinRequest.php.html | 175 + .../Api/V2/StoreCustomerAnswers.php.html | 203 + .../Api/V2/StoreCustomerRequest.php.html | 258 + .../Api/V2/UpdateCustomerRequest.php.html | 267 + .../app/Http/Requests/Api/V2/dashboard.html | 296 + .../app/Http/Requests/Api/V2/index.html | 375 + .../app/Http/Requests/Api/dashboard.html | 295 + .../coverage/app/Http/Requests/Api/index.html | 122 + .../app/Http/Requests/CustomerExport.php.html | 169 + .../Requests/GenerateServiceToken.php.html | 156 + .../Http/Requests/ManageBrandRequest.php.html | 177 + .../Requests/ManageServiceRequest.php.html | 178 + .../app/Http/Requests/QuestionExport.php.html | 158 + .../Requests/StatsByBrandRequest.php.html | 182 + .../Requests/StatsByQuestionRequest.php.html | 182 + .../coverage/app/Http/Requests/dashboard.html | 294 + tests/coverage/app/Http/Requests/index.html | 212 + .../Resources/Api/V2/AnswersResource.php.html | 182 + .../Resources/Api/V2/ApiResponse.php.html | 219 + .../Api/V2/BrandStatsResource.php.html | 189 + .../Api/V2/CustomerResource.php.html | 298 + .../Api/V2/QuestionStatsResource.php.html | 218 + .../Api/V2/QuestionsResource.php.html | 231 + .../app/Http/Resources/Api/V2/dashboard.html | 288 + .../app/Http/Resources/Api/V2/index.html | 263 + .../app/Http/Resources/Api/dashboard.html | 287 + .../app/Http/Resources/Api/index.html | 122 + .../Http/Resources/CustomerResource.php.html | 181 + .../Http/Resources/StatusResource.php.html | 115 + .../app/Http/Resources/dashboard.html | 286 + tests/coverage/app/Http/Resources/index.html | 147 + tests/coverage/app/Http/dashboard.html | 293 + tests/coverage/app/Http/index.html | 217 + .../app/Jobs/SetServiceStats.php.html | 228 + .../coverage/app/Jobs/StartStatJobs.php.html | 146 + tests/coverage/app/Jobs/StatJob.php.html | 221 + .../UpdateCustomerLastConnection.php.html | 187 + tests/coverage/app/Jobs/dashboard.html | 285 + tests/coverage/app/Jobs/index.html | 129 + tests/coverage/app/Lib/Helper.php.html | 494 + .../coverage/app/Lib/MigrateOldData.php.html | 390 + tests/coverage/app/Lib/dashboard.html | 291 + tests/coverage/app/Lib/index.html | 133 + .../app/Mail/CourrierArcadiaError.php.html | 188 + .../Mail/ServiceCredentialRenewed.php.html | 183 + tests/coverage/app/Mail/dashboard.html | 285 + tests/coverage/app/Mail/index.html | 103 + tests/coverage/app/Models/Answer.php.html | 274 + tests/coverage/app/Models/Brand.php.html | 520 + tests/coverage/app/Models/BrandStat.php.html | 236 + tests/coverage/app/Models/Customer.php.html | 554 + .../app/Models/CustomerAnswer.php.html | 236 + .../coverage/app/Models/CustomerInfo.php.html | 357 + .../app/Models/CustomerLogin.php.html | 308 + .../app/Models/CustomerOptInHistory.php.html | 214 + tests/coverage/app/Models/Question.php.html | 317 + tests/coverage/app/Models/Service.php.html | 608 + .../coverage/app/Models/ServiceStat.php.html | 223 + .../coverage/app/Models/SingleOptin.php.html | 274 + tests/coverage/app/Models/User.php.html | 145 + tests/coverage/app/Models/dashboard.html | 310 + tests/coverage/app/Models/index.html | 441 + .../app/Policies/AnswerPolicy.php.html | 146 + .../app/Policies/BrandPolicy.php.html | 146 + .../app/Policies/CustomerPolicy.php.html | 174 + .../app/Policies/QuestionPolicy.php.html | 171 + .../Policies/Rules/UserStaticRules.php.html | 157 + .../app/Policies/Rules/dashboard.html | 286 + tests/coverage/app/Policies/Rules/index.html | 91 + .../app/Policies/ServicePolicy.php.html | 146 + .../app/Policies/SingleOptinPolicy.php.html | 146 + tests/coverage/app/Policies/dashboard.html | 285 + tests/coverage/app/Policies/index.html | 168 + .../app/Providers/AppServiceProvider.php.html | 185 + .../Providers/AuthServiceProvider.php.html | 229 + .../Providers/EventServiceProvider.php.html | 126 + .../Providers/RouteServiceProvider.php.html | 281 + tests/coverage/app/Providers/dashboard.html | 285 + tests/coverage/app/Providers/index.html | 174 + tests/coverage/app/dashboard.html | 329 + tests/coverage/app/index.html | 311 + tests/coverage/clients/V2/dashboard.html | 434 + tests/coverage/clients/V2/index.html | 120 + .../coverage/clients/V2/src/CrmApiV2.php.html | 383 + .../clients/V2/src/Enums/ApiError.php.html | 290 + .../clients/V2/src/Enums/ApiStatus.php.html | 120 + .../V2/src/Enums/ArcadiaMailType.php.html | 183 + .../V2/src/Enums/ArcadiaOrderType.php.html | 183 + .../clients/V2/src/Enums/Country.php.html | 960 + .../clients/V2/src/Enums/HttpMethods.php.html | 120 + .../clients/V2/src/Enums/LogLevel.php.html | 195 + .../V2/src/Enums/QuestionType.php.html | 185 + .../clients/V2/src/Enums/dashboard.html | 291 + .../coverage/clients/V2/src/Enums/index.html | 288 + .../V2/src/Exceptions/AccessError.php.html | 180 + .../src/Exceptions/ApiErrorException.php.html | 322 + .../V2/src/Exceptions/CrmException.php.html | 144 + .../SkipValidationException.php.html | 119 + .../Exceptions/ValidationException.php.html | 143 + .../WpCrmInvalidJsonUserInfo.php.html | 182 + .../clients/V2/src/Exceptions/dashboard.html | 293 + .../clients/V2/src/Exceptions/index.html | 217 + .../clients/V2/src/Models/Answer.php.html | 331 + .../V2/src/Models/ArcadiaMailDefault.php.html | 689 + .../V2/src/Models/ArcadiaMailGame.php.html | 589 + .../V2/src/Models/CrmClientModel.php.html | 736 + .../clients/V2/src/Models/Customer.php.html | 1069 + .../clients/V2/src/Models/Question.php.html | 631 + .../V2/src/Models/SingleOptin.php.html | 207 + .../clients/V2/src/Models/Stats.php.html | 263 + .../clients/V2/src/Models/dashboard.html | 326 + .../coverage/clients/V2/src/Models/index.html | 318 + .../V2/src/Transport/ApiInterface.php.html | 516 + .../clients/V2/src/Transport/Request.php.html | 477 + .../V2/src/Transport/Response.php.html | 419 + .../clients/V2/src/Transport/dashboard.html | 305 + .../clients/V2/src/Transport/index.html | 178 + tests/coverage/clients/V2/src/Utils.php.html | 1373 ++ .../clients/V2/src/WordPress/WPCrm.php.html | 1269 ++ .../V2/src/WordPress/WPCrmAnswer.php.html | 203 + .../V2/src/WordPress/WPCrmQuestion.php.html | 286 + .../V2/src/WordPress/WPCrmUser.php.html | 615 + .../clients/V2/src/WordPress/dashboard.html | 350 + .../clients/V2/src/WordPress/index.html | 206 + tests/coverage/clients/V2/src/dashboard.html | 435 + tests/coverage/clients/V2/src/index.html | 289 + tests/coverage/clients/dashboard.html | 433 + tests/coverage/clients/index.html | 119 + tests/coverage/dashboard.html | 328 + tests/coverage/index.html | 310 + 564 files changed, 119050 insertions(+), 3593 deletions(-) delete mode 100644 CRM V2.code-workspace delete mode 100644 app/Http/Requests/Api/V2/StoreSingleOptinRequest.php delete mode 100644 app/Http/Requests/Api/V2/UpdateSingleOptinRequest.php rename app/Http/Requests/{GenerateApiToken.php => GenerateServiceToken.php} (76%) delete mode 100644 app/Http/Resources/Api/V2/QuestionResource.php delete mode 100644 app/Http/Resources/Api/V2/SingleOptinResource.php create mode 100644 app/Http/Sanitizers/RemoveSpaces.php delete mode 100644 app/Providers/BroadcastServiceProvider.php create mode 100644 database/migrations/2021_12_07_203437_drop_api_token_from_services.php create mode 100644 database/migrations/2021_12_07_211411_set_unique_constrain_on_api_token_on_services.php create mode 100644 database/migrations/2021_12_08_000551_increase_bearer_pwd_on_services.php create mode 100644 database/migrations/2021_12_26_152449_parse_country_on_customer_infos.php create mode 100644 database/migrations/2021_12_28_010744_set_customer_id_nullable_on_customer_answer.php create mode 100644 package-lock.json delete mode 100644 routes/channels.php rename tests/{Feature => CRM}/ApiV2/ApiBase.php (58%) create mode 100644 tests/CRM/ApiV2/ApiTest.php rename tests/{Feature => CRM}/ApiV2/ArcadiaTest.php (92%) create mode 100644 tests/CRM/ApiV2/CustomerTest.php create mode 100644 tests/CRM/ApiV2/QuestionTest.php rename tests/{Feature => CRM}/ApiV2/SingleOptinTest.php (54%) create mode 100644 tests/CRM/ApiV2/StatsTest.php rename tests/{Unit => Client}/CrmClientTester.php (59%) rename tests/{Unit => Client}/CrmClientV1CustomerTest.php (76%) create mode 100644 tests/Client/CrmClientV2ArcadiaTest.php rename tests/{Unit => Client}/CrmClientV2ClientTest.php (60%) rename tests/{Unit => Client}/CrmClientV2ConfigTest.php (67%) rename tests/{Unit => Client}/CrmClientV2CustomerTest.php (51%) create mode 100644 tests/Client/CrmClientV2QuestionTest.php rename tests/{Unit => Client}/CrmClientV2RouterTest.php (76%) create mode 100644 tests/Client/CrmClientV2SingleOptinTest.php rename tests/{Unit => Client}/CrmClientV2StatsTest.php (85%) rename tests/{Unit => Client}/HelperTest.php (71%) create mode 100644 tests/Client/UtilsTest.php delete mode 100644 tests/Feature/ApiV2/ApiTest.php delete mode 100644 tests/Feature/ApiV2/CustomerTest.php delete mode 100644 tests/Feature/ApiV2/QuestionTest.php delete mode 100644 tests/Feature/ApiV2/StatsTest.php create mode 100644 tests/StressTest/RunMultipleRequestsTest.php delete mode 100644 tests/Unit/CrmClientV2ArcadiaTest.php delete mode 100644 tests/Unit/CrmClientV2QuestionTest.php delete mode 100644 tests/Unit/CrmClientV2SingleOptinTest.php create mode 100644 tests/coverage/Console/Commands/DeleteUnconfirmedCustomers.php.html create mode 100644 tests/coverage/Console/Commands/GenerateCrmClient.php.html create mode 100644 tests/coverage/Console/Commands/LdcWebStatusChecker.php.html create mode 100644 tests/coverage/Console/Commands/MigrateCrmUsers.php.html create mode 100644 tests/coverage/Console/Commands/StartStatJobs.php.html create mode 100644 tests/coverage/Console/Commands/UserCreate.php.html create mode 100644 tests/coverage/Console/Commands/dashboard.html create mode 100644 tests/coverage/Console/Commands/index.html create mode 100644 tests/coverage/Console/Kernel.php.html create mode 100644 tests/coverage/Console/dashboard.html create mode 100644 tests/coverage/Console/index.html create mode 100644 tests/coverage/Enums/ApiError.php.html create mode 100644 tests/coverage/Enums/ApiStatus.php.html create mode 100644 tests/coverage/Enums/ArcadiaMailType.php.html create mode 100644 tests/coverage/Enums/ArcadiaOrderType.php.html create mode 100644 tests/coverage/Enums/Country.php.html create mode 100644 tests/coverage/Enums/CustomerExportType.php.html create mode 100644 tests/coverage/Enums/QuestionType.php.html create mode 100644 tests/coverage/Enums/dashboard.html create mode 100644 tests/coverage/Enums/index.html create mode 100644 tests/coverage/Exceptions/AbortRowImportException.php.html create mode 100644 tests/coverage/Exceptions/Api/V2/ApiErrorException.php.html create mode 100644 tests/coverage/Exceptions/Api/V2/CrmException.php.html create mode 100644 tests/coverage/Exceptions/Api/V2/dashboard.html create mode 100644 tests/coverage/Exceptions/Api/V2/index.html create mode 100644 tests/coverage/Exceptions/Api/dashboard.html create mode 100644 tests/coverage/Exceptions/Api/index.html create mode 100644 tests/coverage/Exceptions/Handler.php.html create mode 100644 tests/coverage/Exceptions/dashboard.html create mode 100644 tests/coverage/Exceptions/index.html create mode 100644 tests/coverage/Http/Controllers/AnswerController.php.html create mode 100644 tests/coverage/Http/Controllers/Api/V1/ArcadiaController.php.html create mode 100644 tests/coverage/Http/Controllers/Api/V1/CustomerController.php.html create mode 100644 tests/coverage/Http/Controllers/Api/V1/dashboard.html create mode 100644 tests/coverage/Http/Controllers/Api/V1/index.html create mode 100644 tests/coverage/Http/Controllers/Api/V2/AnswerController.php.html create mode 100644 tests/coverage/Http/Controllers/Api/V2/ApiController.php.html create mode 100644 tests/coverage/Http/Controllers/Api/V2/ArcadiaController.php.html create mode 100644 tests/coverage/Http/Controllers/Api/V2/CustomerController.php.html create mode 100644 tests/coverage/Http/Controllers/Api/V2/OptinController.php.html create mode 100644 tests/coverage/Http/Controllers/Api/V2/QuestionController.php.html create mode 100644 tests/coverage/Http/Controllers/Api/V2/StatsController.php.html create mode 100644 tests/coverage/Http/Controllers/Api/V2/dashboard.html create mode 100644 tests/coverage/Http/Controllers/Api/V2/index.html create mode 100644 tests/coverage/Http/Controllers/Api/dashboard.html create mode 100644 tests/coverage/Http/Controllers/Api/index.html create mode 100644 tests/coverage/Http/Controllers/BrandController.php.html create mode 100644 tests/coverage/Http/Controllers/Controller.php.html create mode 100644 tests/coverage/Http/Controllers/CustomerController.php.html create mode 100644 tests/coverage/Http/Controllers/LoginController.php.html create mode 100644 tests/coverage/Http/Controllers/QuestionController.php.html create mode 100644 tests/coverage/Http/Controllers/ServiceController.php.html create mode 100644 tests/coverage/Http/Controllers/SingleOptinController.php.html create mode 100644 tests/coverage/Http/Controllers/StatsController.php.html create mode 100644 tests/coverage/Http/Controllers/dashboard.html create mode 100644 tests/coverage/Http/Controllers/index.html create mode 100644 tests/coverage/Http/Kernel.php.html create mode 100644 tests/coverage/Http/Middleware/ApiCheckTimeStamps.php.html create mode 100644 tests/coverage/Http/Middleware/ApiResponse.php.html create mode 100644 tests/coverage/Http/Middleware/Authenticate.php.html create mode 100644 tests/coverage/Http/Middleware/CheckBrand.php.html create mode 100644 tests/coverage/Http/Middleware/EncryptCookies.php.html create mode 100644 tests/coverage/Http/Middleware/JsonOnly.php.html create mode 100644 tests/coverage/Http/Middleware/PreventRequestsDuringMaintenance.php.html create mode 100644 tests/coverage/Http/Middleware/RedirectIfAuthenticated.php.html create mode 100644 tests/coverage/Http/Middleware/TrimStrings.php.html create mode 100644 tests/coverage/Http/Middleware/TrustHosts.php.html create mode 100644 tests/coverage/Http/Middleware/TrustProxies.php.html create mode 100644 tests/coverage/Http/Middleware/VerifyCsrfToken.php.html create mode 100644 tests/coverage/Http/Middleware/XssSanitizer.php.html create mode 100644 tests/coverage/Http/Middleware/dashboard.html create mode 100644 tests/coverage/Http/Middleware/index.html create mode 100644 tests/coverage/Http/Requests/Api/V2/AuthenticateRequest.php.html create mode 100644 tests/coverage/Http/Requests/Api/V2/BaseApiRequest.php.html create mode 100644 tests/coverage/Http/Requests/Api/V2/GetBrandStatsRequest.php.html create mode 100644 tests/coverage/Http/Requests/Api/V2/GetQuestionStatsRequest.php.html create mode 100644 tests/coverage/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php.html create mode 100644 tests/coverage/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php.html create mode 100644 tests/coverage/Http/Requests/Api/V2/SingleOptinRequest.php.html create mode 100644 tests/coverage/Http/Requests/Api/V2/StoreCustomerAnswers.php.html create mode 100644 tests/coverage/Http/Requests/Api/V2/StoreCustomerRequest.php.html create mode 100644 tests/coverage/Http/Requests/Api/V2/UpdateCustomerRequest.php.html create mode 100644 tests/coverage/Http/Requests/Api/V2/dashboard.html create mode 100644 tests/coverage/Http/Requests/Api/V2/index.html create mode 100644 tests/coverage/Http/Requests/Api/dashboard.html create mode 100644 tests/coverage/Http/Requests/Api/index.html create mode 100644 tests/coverage/Http/Requests/CustomerExport.php.html create mode 100644 tests/coverage/Http/Requests/GenerateServiceToken.php.html create mode 100644 tests/coverage/Http/Requests/ManageBrandRequest.php.html create mode 100644 tests/coverage/Http/Requests/ManageServiceRequest.php.html create mode 100644 tests/coverage/Http/Requests/QuestionExport.php.html create mode 100644 tests/coverage/Http/Requests/StatsByBrandRequest.php.html create mode 100644 tests/coverage/Http/Requests/StatsByQuestionRequest.php.html create mode 100644 tests/coverage/Http/Requests/dashboard.html create mode 100644 tests/coverage/Http/Requests/index.html create mode 100644 tests/coverage/Http/Resources/Api/V2/AnswersResource.php.html create mode 100644 tests/coverage/Http/Resources/Api/V2/ApiResponse.php.html create mode 100644 tests/coverage/Http/Resources/Api/V2/BrandStatsResource.php.html create mode 100644 tests/coverage/Http/Resources/Api/V2/CustomerResource.php.html create mode 100644 tests/coverage/Http/Resources/Api/V2/QuestionStatsResource.php.html create mode 100644 tests/coverage/Http/Resources/Api/V2/QuestionsResource.php.html create mode 100644 tests/coverage/Http/Resources/Api/V2/dashboard.html create mode 100644 tests/coverage/Http/Resources/Api/V2/index.html create mode 100644 tests/coverage/Http/Resources/Api/dashboard.html create mode 100644 tests/coverage/Http/Resources/Api/index.html create mode 100644 tests/coverage/Http/Resources/CustomerResource.php.html create mode 100644 tests/coverage/Http/Resources/StatusResource.php.html create mode 100644 tests/coverage/Http/Resources/dashboard.html create mode 100644 tests/coverage/Http/Resources/index.html create mode 100644 tests/coverage/Http/Sanitizers/RemoveSpaces.php.html create mode 100644 tests/coverage/Http/Sanitizers/dashboard.html create mode 100644 tests/coverage/Http/Sanitizers/index.html create mode 100644 tests/coverage/Http/dashboard.html create mode 100644 tests/coverage/Http/index.html create mode 100644 tests/coverage/Jobs/SetServiceStats.php.html create mode 100644 tests/coverage/Jobs/StartStatJobs.php.html create mode 100644 tests/coverage/Jobs/StatJob.php.html create mode 100644 tests/coverage/Jobs/UpdateCustomerLastConnection.php.html create mode 100644 tests/coverage/Jobs/dashboard.html create mode 100644 tests/coverage/Jobs/index.html create mode 100644 tests/coverage/Lib/Helper.php.html create mode 100644 tests/coverage/Lib/MigrateOldData.php.html create mode 100644 tests/coverage/Lib/dashboard.html create mode 100644 tests/coverage/Lib/index.html create mode 100644 tests/coverage/Mail/CourrierArcadiaError.php.html create mode 100644 tests/coverage/Mail/ServiceCredentialRenewed.php.html create mode 100644 tests/coverage/Mail/dashboard.html create mode 100644 tests/coverage/Mail/index.html create mode 100644 tests/coverage/Models/Answer.php.html create mode 100644 tests/coverage/Models/Brand.php.html create mode 100644 tests/coverage/Models/BrandStat.php.html create mode 100644 tests/coverage/Models/Customer.php.html create mode 100644 tests/coverage/Models/CustomerAnswer.php.html create mode 100644 tests/coverage/Models/CustomerInfo.php.html create mode 100644 tests/coverage/Models/CustomerLogin.php.html create mode 100644 tests/coverage/Models/CustomerOptInHistory.php.html create mode 100644 tests/coverage/Models/Question.php.html create mode 100644 tests/coverage/Models/Service.php.html create mode 100644 tests/coverage/Models/ServiceStat.php.html create mode 100644 tests/coverage/Models/SingleOptin.php.html create mode 100644 tests/coverage/Models/User.php.html create mode 100644 tests/coverage/Models/dashboard.html create mode 100644 tests/coverage/Models/index.html create mode 100644 tests/coverage/Policies/AnswerPolicy.php.html create mode 100644 tests/coverage/Policies/BrandPolicy.php.html create mode 100644 tests/coverage/Policies/CustomerPolicy.php.html create mode 100644 tests/coverage/Policies/QuestionPolicy.php.html create mode 100644 tests/coverage/Policies/Rules/UserStaticRules.php.html create mode 100644 tests/coverage/Policies/Rules/dashboard.html create mode 100644 tests/coverage/Policies/Rules/index.html create mode 100644 tests/coverage/Policies/ServicePolicy.php.html create mode 100644 tests/coverage/Policies/SingleOptinPolicy.php.html create mode 100644 tests/coverage/Policies/dashboard.html create mode 100644 tests/coverage/Policies/index.html create mode 100644 tests/coverage/Providers/AppServiceProvider.php.html create mode 100644 tests/coverage/Providers/AuthServiceProvider.php.html create mode 100644 tests/coverage/Providers/EventServiceProvider.php.html create mode 100644 tests/coverage/Providers/RouteServiceProvider.php.html create mode 100644 tests/coverage/Providers/dashboard.html create mode 100644 tests/coverage/Providers/index.html create mode 100644 tests/coverage/_css/bootstrap.min.css create mode 100644 tests/coverage/_css/custom.css create mode 100644 tests/coverage/_css/nv.d3.min.css create mode 100644 tests/coverage/_css/octicons.css create mode 100644 tests/coverage/_css/style.css create mode 100644 tests/coverage/_icons/file-code.svg create mode 100644 tests/coverage/_icons/file-directory.svg create mode 100644 tests/coverage/_js/bootstrap.min.js create mode 100644 tests/coverage/_js/d3.min.js create mode 100644 tests/coverage/_js/file.js create mode 100644 tests/coverage/_js/jquery.min.js create mode 100644 tests/coverage/_js/nv.d3.min.js create mode 100644 tests/coverage/_js/popper.min.js create mode 100644 tests/coverage/app/Console/Commands/DeleteUnconfirmedCustomers.php.html create mode 100644 tests/coverage/app/Console/Commands/GenerateCrmClient.php.html create mode 100644 tests/coverage/app/Console/Commands/LdcWebStatusChecker.php.html create mode 100644 tests/coverage/app/Console/Commands/MigrateCrmUsers.php.html create mode 100644 tests/coverage/app/Console/Commands/StartStatJobs.php.html create mode 100644 tests/coverage/app/Console/Commands/UserCreate.php.html create mode 100644 tests/coverage/app/Console/Commands/dashboard.html create mode 100644 tests/coverage/app/Console/Commands/index.html create mode 100644 tests/coverage/app/Console/Kernel.php.html create mode 100644 tests/coverage/app/Console/dashboard.html create mode 100644 tests/coverage/app/Console/index.html create mode 100644 tests/coverage/app/Enums/ApiError.php.html create mode 100644 tests/coverage/app/Enums/ApiStatus.php.html create mode 100644 tests/coverage/app/Enums/ArcadiaMailType.php.html create mode 100644 tests/coverage/app/Enums/ArcadiaOrderType.php.html create mode 100644 tests/coverage/app/Enums/Country.php.html create mode 100644 tests/coverage/app/Enums/CustomerExportType.php.html create mode 100644 tests/coverage/app/Enums/QuestionType.php.html create mode 100644 tests/coverage/app/Enums/dashboard.html create mode 100644 tests/coverage/app/Enums/index.html create mode 100644 tests/coverage/app/Exceptions/AbortRowImportException.php.html create mode 100644 tests/coverage/app/Exceptions/Api/V2/ApiErrorException.php.html create mode 100644 tests/coverage/app/Exceptions/Api/V2/CrmException.php.html create mode 100644 tests/coverage/app/Exceptions/Api/V2/dashboard.html create mode 100644 tests/coverage/app/Exceptions/Api/V2/index.html create mode 100644 tests/coverage/app/Exceptions/Api/dashboard.html create mode 100644 tests/coverage/app/Exceptions/Api/index.html create mode 100644 tests/coverage/app/Exceptions/Handler.php.html create mode 100644 tests/coverage/app/Exceptions/dashboard.html create mode 100644 tests/coverage/app/Exceptions/index.html create mode 100644 tests/coverage/app/Http/Controllers/AnswerController.php.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V1/ArcadiaController.php.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V1/CustomerController.php.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V1/dashboard.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V1/index.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V2/AnswerController.php.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V2/ApiController.php.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V2/ArcadiaController.php.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V2/CustomerController.php.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V2/OptinController.php.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V2/QuestionController.php.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V2/StatsController.php.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V2/dashboard.html create mode 100644 tests/coverage/app/Http/Controllers/Api/V2/index.html create mode 100644 tests/coverage/app/Http/Controllers/Api/dashboard.html create mode 100644 tests/coverage/app/Http/Controllers/Api/index.html create mode 100644 tests/coverage/app/Http/Controllers/BrandController.php.html create mode 100644 tests/coverage/app/Http/Controllers/Controller.php.html create mode 100644 tests/coverage/app/Http/Controllers/CustomerController.php.html create mode 100644 tests/coverage/app/Http/Controllers/LoginController.php.html create mode 100644 tests/coverage/app/Http/Controllers/QuestionController.php.html create mode 100644 tests/coverage/app/Http/Controllers/ServiceController.php.html create mode 100644 tests/coverage/app/Http/Controllers/SingleOptinController.php.html create mode 100644 tests/coverage/app/Http/Controllers/StatsController.php.html create mode 100644 tests/coverage/app/Http/Controllers/dashboard.html create mode 100644 tests/coverage/app/Http/Controllers/index.html create mode 100644 tests/coverage/app/Http/Kernel.php.html create mode 100644 tests/coverage/app/Http/Middleware/ApiCheckTimeStamps.php.html create mode 100644 tests/coverage/app/Http/Middleware/ApiResponse.php.html create mode 100644 tests/coverage/app/Http/Middleware/Authenticate.php.html create mode 100644 tests/coverage/app/Http/Middleware/CheckBrand.php.html create mode 100644 tests/coverage/app/Http/Middleware/EncryptCookies.php.html create mode 100644 tests/coverage/app/Http/Middleware/JsonOnly.php.html create mode 100644 tests/coverage/app/Http/Middleware/PreventRequestsDuringMaintenance.php.html create mode 100644 tests/coverage/app/Http/Middleware/RedirectIfAuthenticated.php.html create mode 100644 tests/coverage/app/Http/Middleware/TrimStrings.php.html create mode 100644 tests/coverage/app/Http/Middleware/TrustHosts.php.html create mode 100644 tests/coverage/app/Http/Middleware/TrustProxies.php.html create mode 100644 tests/coverage/app/Http/Middleware/VerifyCsrfToken.php.html create mode 100644 tests/coverage/app/Http/Middleware/XssSanitizer.php.html create mode 100644 tests/coverage/app/Http/Middleware/dashboard.html create mode 100644 tests/coverage/app/Http/Middleware/index.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/AuthenticateRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/BaseApiRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/GetBrandStatsRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/GetQuestionStatsRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/SingleOptinRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/StoreCustomerAnswers.php.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/StoreCustomerRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/UpdateCustomerRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/dashboard.html create mode 100644 tests/coverage/app/Http/Requests/Api/V2/index.html create mode 100644 tests/coverage/app/Http/Requests/Api/dashboard.html create mode 100644 tests/coverage/app/Http/Requests/Api/index.html create mode 100644 tests/coverage/app/Http/Requests/CustomerExport.php.html create mode 100644 tests/coverage/app/Http/Requests/GenerateServiceToken.php.html create mode 100644 tests/coverage/app/Http/Requests/ManageBrandRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/ManageServiceRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/QuestionExport.php.html create mode 100644 tests/coverage/app/Http/Requests/StatsByBrandRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/StatsByQuestionRequest.php.html create mode 100644 tests/coverage/app/Http/Requests/dashboard.html create mode 100644 tests/coverage/app/Http/Requests/index.html create mode 100644 tests/coverage/app/Http/Resources/Api/V2/AnswersResource.php.html create mode 100644 tests/coverage/app/Http/Resources/Api/V2/ApiResponse.php.html create mode 100644 tests/coverage/app/Http/Resources/Api/V2/BrandStatsResource.php.html create mode 100644 tests/coverage/app/Http/Resources/Api/V2/CustomerResource.php.html create mode 100644 tests/coverage/app/Http/Resources/Api/V2/QuestionStatsResource.php.html create mode 100644 tests/coverage/app/Http/Resources/Api/V2/QuestionsResource.php.html create mode 100644 tests/coverage/app/Http/Resources/Api/V2/dashboard.html create mode 100644 tests/coverage/app/Http/Resources/Api/V2/index.html create mode 100644 tests/coverage/app/Http/Resources/Api/dashboard.html create mode 100644 tests/coverage/app/Http/Resources/Api/index.html create mode 100644 tests/coverage/app/Http/Resources/CustomerResource.php.html create mode 100644 tests/coverage/app/Http/Resources/StatusResource.php.html create mode 100644 tests/coverage/app/Http/Resources/dashboard.html create mode 100644 tests/coverage/app/Http/Resources/index.html create mode 100644 tests/coverage/app/Http/dashboard.html create mode 100644 tests/coverage/app/Http/index.html create mode 100644 tests/coverage/app/Jobs/SetServiceStats.php.html create mode 100644 tests/coverage/app/Jobs/StartStatJobs.php.html create mode 100644 tests/coverage/app/Jobs/StatJob.php.html create mode 100644 tests/coverage/app/Jobs/UpdateCustomerLastConnection.php.html create mode 100644 tests/coverage/app/Jobs/dashboard.html create mode 100644 tests/coverage/app/Jobs/index.html create mode 100644 tests/coverage/app/Lib/Helper.php.html create mode 100644 tests/coverage/app/Lib/MigrateOldData.php.html create mode 100644 tests/coverage/app/Lib/dashboard.html create mode 100644 tests/coverage/app/Lib/index.html create mode 100644 tests/coverage/app/Mail/CourrierArcadiaError.php.html create mode 100644 tests/coverage/app/Mail/ServiceCredentialRenewed.php.html create mode 100644 tests/coverage/app/Mail/dashboard.html create mode 100644 tests/coverage/app/Mail/index.html create mode 100644 tests/coverage/app/Models/Answer.php.html create mode 100644 tests/coverage/app/Models/Brand.php.html create mode 100644 tests/coverage/app/Models/BrandStat.php.html create mode 100644 tests/coverage/app/Models/Customer.php.html create mode 100644 tests/coverage/app/Models/CustomerAnswer.php.html create mode 100644 tests/coverage/app/Models/CustomerInfo.php.html create mode 100644 tests/coverage/app/Models/CustomerLogin.php.html create mode 100644 tests/coverage/app/Models/CustomerOptInHistory.php.html create mode 100644 tests/coverage/app/Models/Question.php.html create mode 100644 tests/coverage/app/Models/Service.php.html create mode 100644 tests/coverage/app/Models/ServiceStat.php.html create mode 100644 tests/coverage/app/Models/SingleOptin.php.html create mode 100644 tests/coverage/app/Models/User.php.html create mode 100644 tests/coverage/app/Models/dashboard.html create mode 100644 tests/coverage/app/Models/index.html create mode 100644 tests/coverage/app/Policies/AnswerPolicy.php.html create mode 100644 tests/coverage/app/Policies/BrandPolicy.php.html create mode 100644 tests/coverage/app/Policies/CustomerPolicy.php.html create mode 100644 tests/coverage/app/Policies/QuestionPolicy.php.html create mode 100644 tests/coverage/app/Policies/Rules/UserStaticRules.php.html create mode 100644 tests/coverage/app/Policies/Rules/dashboard.html create mode 100644 tests/coverage/app/Policies/Rules/index.html create mode 100644 tests/coverage/app/Policies/ServicePolicy.php.html create mode 100644 tests/coverage/app/Policies/SingleOptinPolicy.php.html create mode 100644 tests/coverage/app/Policies/dashboard.html create mode 100644 tests/coverage/app/Policies/index.html create mode 100644 tests/coverage/app/Providers/AppServiceProvider.php.html create mode 100644 tests/coverage/app/Providers/AuthServiceProvider.php.html create mode 100644 tests/coverage/app/Providers/EventServiceProvider.php.html create mode 100644 tests/coverage/app/Providers/RouteServiceProvider.php.html create mode 100644 tests/coverage/app/Providers/dashboard.html create mode 100644 tests/coverage/app/Providers/index.html create mode 100644 tests/coverage/app/dashboard.html create mode 100644 tests/coverage/app/index.html create mode 100644 tests/coverage/clients/V2/dashboard.html create mode 100644 tests/coverage/clients/V2/index.html create mode 100644 tests/coverage/clients/V2/src/CrmApiV2.php.html create mode 100644 tests/coverage/clients/V2/src/Enums/ApiError.php.html create mode 100644 tests/coverage/clients/V2/src/Enums/ApiStatus.php.html create mode 100644 tests/coverage/clients/V2/src/Enums/ArcadiaMailType.php.html create mode 100644 tests/coverage/clients/V2/src/Enums/ArcadiaOrderType.php.html create mode 100644 tests/coverage/clients/V2/src/Enums/Country.php.html create mode 100644 tests/coverage/clients/V2/src/Enums/HttpMethods.php.html create mode 100644 tests/coverage/clients/V2/src/Enums/LogLevel.php.html create mode 100644 tests/coverage/clients/V2/src/Enums/QuestionType.php.html create mode 100644 tests/coverage/clients/V2/src/Enums/dashboard.html create mode 100644 tests/coverage/clients/V2/src/Enums/index.html create mode 100644 tests/coverage/clients/V2/src/Exceptions/AccessError.php.html create mode 100644 tests/coverage/clients/V2/src/Exceptions/ApiErrorException.php.html create mode 100644 tests/coverage/clients/V2/src/Exceptions/CrmException.php.html create mode 100644 tests/coverage/clients/V2/src/Exceptions/SkipValidationException.php.html create mode 100644 tests/coverage/clients/V2/src/Exceptions/ValidationException.php.html create mode 100644 tests/coverage/clients/V2/src/Exceptions/WpCrmInvalidJsonUserInfo.php.html create mode 100644 tests/coverage/clients/V2/src/Exceptions/dashboard.html create mode 100644 tests/coverage/clients/V2/src/Exceptions/index.html create mode 100644 tests/coverage/clients/V2/src/Models/Answer.php.html create mode 100644 tests/coverage/clients/V2/src/Models/ArcadiaMailDefault.php.html create mode 100644 tests/coverage/clients/V2/src/Models/ArcadiaMailGame.php.html create mode 100644 tests/coverage/clients/V2/src/Models/CrmClientModel.php.html create mode 100644 tests/coverage/clients/V2/src/Models/Customer.php.html create mode 100644 tests/coverage/clients/V2/src/Models/Question.php.html create mode 100644 tests/coverage/clients/V2/src/Models/SingleOptin.php.html create mode 100644 tests/coverage/clients/V2/src/Models/Stats.php.html create mode 100644 tests/coverage/clients/V2/src/Models/dashboard.html create mode 100644 tests/coverage/clients/V2/src/Models/index.html create mode 100644 tests/coverage/clients/V2/src/Transport/ApiInterface.php.html create mode 100644 tests/coverage/clients/V2/src/Transport/Request.php.html create mode 100644 tests/coverage/clients/V2/src/Transport/Response.php.html create mode 100644 tests/coverage/clients/V2/src/Transport/dashboard.html create mode 100644 tests/coverage/clients/V2/src/Transport/index.html create mode 100644 tests/coverage/clients/V2/src/Utils.php.html create mode 100644 tests/coverage/clients/V2/src/WordPress/WPCrm.php.html create mode 100644 tests/coverage/clients/V2/src/WordPress/WPCrmAnswer.php.html create mode 100644 tests/coverage/clients/V2/src/WordPress/WPCrmQuestion.php.html create mode 100644 tests/coverage/clients/V2/src/WordPress/WPCrmUser.php.html create mode 100644 tests/coverage/clients/V2/src/WordPress/dashboard.html create mode 100644 tests/coverage/clients/V2/src/WordPress/index.html create mode 100644 tests/coverage/clients/V2/src/dashboard.html create mode 100644 tests/coverage/clients/V2/src/index.html create mode 100644 tests/coverage/clients/dashboard.html create mode 100644 tests/coverage/clients/index.html create mode 100644 tests/coverage/dashboard.html create mode 100644 tests/coverage/index.html diff --git a/.gitignore b/.gitignore index de543e8..543f440 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,6 @@ /vendor .vscode/ .idea/ -package-lock.json -composer.lock .env .env.testing .env.backup diff --git a/CRM V2.code-workspace b/CRM V2.code-workspace deleted file mode 100644 index b51044b..0000000 --- a/CRM V2.code-workspace +++ /dev/null @@ -1,7 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ] -} diff --git a/README.md b/README.md index fbd3cd8..073d7e2 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,6 @@ ### 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 -Prérequis : - - Avoir un serveur memcached sur 127.0.0.1:11211 - - Avoir l'extension memcached - Etapes : - cp .env.testing.example .env.testing (spécial tests unitaires) - php artisan key:generate @@ -14,7 +10,7 @@ Etapes : - git submodule init - git submodule update (récupérer le client) - composer install -- service start memcached - 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 diff --git a/app/Console/Commands/DeleteUnconfirmedCustomers.php b/app/Console/Commands/DeleteUnconfirmedCustomers.php index 421fa39..2dd4316 100644 --- a/app/Console/Commands/DeleteUnconfirmedCustomers.php +++ b/app/Console/Commands/DeleteUnconfirmedCustomers.php @@ -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; } } diff --git a/app/Console/Commands/GenerateCrmClient.php b/app/Console/Commands/GenerateCrmClient.php index 73446fc..7ba2838 100644 --- a/app/Console/Commands/GenerateCrmClient.php +++ b/app/Console/Commands/GenerateCrmClient.php @@ -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)); diff --git a/app/Console/Commands/LdcWebStatusChecker.php b/app/Console/Commands/LdcWebStatusChecker.php index 563db63..1ffec37 100644 --- a/app/Console/Commands/LdcWebStatusChecker.php +++ b/app/Console/Commands/LdcWebStatusChecker.php @@ -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); diff --git a/app/Console/Commands/MigrateCrmUsers.php b/app/Console/Commands/MigrateCrmUsers.php index 8e50097..eb7f5f5 100644 --- a/app/Console/Commands/MigrateCrmUsers.php +++ b/app/Console/Commands/MigrateCrmUsers.php @@ -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; } } diff --git a/app/Console/Commands/StartStatJobs.php b/app/Console/Commands/StartStatJobs.php index 2a06a3e..08efb4c 100644 --- a/app/Console/Commands/StartStatJobs.php +++ b/app/Console/Commands/StartStatJobs.php @@ -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() { diff --git a/app/Console/Commands/UserCreate.php b/app/Console/Commands/UserCreate.php index ef5fd5b..a198eda 100644 --- a/app/Console/Commands/UserCreate.php +++ b/app/Console/Commands/UserCreate.php @@ -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; } } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 1985a09..9da06b1 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -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'); } diff --git a/app/Enums/ApiError.php b/app/Enums/ApiError.php index f381e49..2a30cb2 100644 --- a/app/Enums/ApiError.php +++ b/app/Enums/ApiError.php @@ -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', diff --git a/app/Enums/ArcadiaMailType.php b/app/Enums/ArcadiaMailType.php index b56000b..2def53c 100644 --- a/app/Enums/ArcadiaMailType.php +++ b/app/Enums/ArcadiaMailType.php @@ -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 [ diff --git a/app/Enums/ArcadiaOrderType.php b/app/Enums/ArcadiaOrderType.php index 5ff85ec..1613874 100644 --- a/app/Enums/ArcadiaOrderType.php +++ b/app/Enums/ArcadiaOrderType.php @@ -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 [ diff --git a/app/Enums/Country.php b/app/Enums/Country.php index 0d0fff6..a438fd8 100644 --- a/app/Enums/Country.php +++ b/app/Enums/Country.php @@ -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 [ diff --git a/app/Enums/CustomerExportType.php b/app/Enums/CustomerExportType.php index 89ce1c1..1d74d97 100644 --- a/app/Enums/CustomerExportType.php +++ b/app/Enums/CustomerExportType.php @@ -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 [ diff --git a/app/Enums/QuestionType.php b/app/Enums/QuestionType.php index 0fb24d4..d4eac15 100644 --- a/app/Enums/QuestionType.php +++ b/app/Enums/QuestionType.php @@ -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 [ diff --git a/app/Exceptions/Api/V2/ApiErrorException.php b/app/Exceptions/Api/V2/ApiErrorException.php index 63e839a..b9345fe 100644 --- a/app/Exceptions/Api/V2/ApiErrorException.php +++ b/app/Exceptions/Api/V2/ApiErrorException.php @@ -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 } } diff --git a/app/Exceptions/Api/V2/CrmException.php b/app/Exceptions/Api/V2/CrmException.php index f720c9f..3a386b0 100644 --- a/app/Exceptions/Api/V2/CrmException.php +++ b/app/Exceptions/Api/V2/CrmException.php @@ -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())); diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 0940f66..c3f441d 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -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); } diff --git a/app/Http/Controllers/AnswerController.php b/app/Http/Controllers/AnswerController.php index 976befe..ee40ebc 100644 --- a/app/Http/Controllers/AnswerController.php +++ b/app/Http/Controllers/AnswerController.php @@ -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 */ diff --git a/app/Http/Controllers/Api/V1/ArcadiaController.php b/app/Http/Controllers/Api/V1/ArcadiaController.php index 3ac0368..ef7d4ad 100644 --- a/app/Http/Controllers/Api/V1/ArcadiaController.php +++ b/app/Http/Controllers/Api/V1/ArcadiaController.php @@ -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')); diff --git a/app/Http/Controllers/Api/V1/CustomerController.php b/app/Http/Controllers/Api/V1/CustomerController.php index 0482273..95ff98e 100644 --- a/app/Http/Controllers/Api/V1/CustomerController.php +++ b/app/Http/Controllers/Api/V1/CustomerController.php @@ -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([ diff --git a/app/Http/Controllers/Api/V2/AnswerController.php b/app/Http/Controllers/Api/V2/AnswerController.php index c5c402b..6b5ebe1 100644 --- a/app/Http/Controllers/Api/V2/AnswerController.php +++ b/app/Http/Controllers/Api/V2/AnswerController.php @@ -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(); } } diff --git a/app/Http/Controllers/Api/V2/ApiController.php b/app/Http/Controllers/Api/V2/ApiController.php index 593058a..e4b6a94 100644 --- a/app/Http/Controllers/Api/V2/ApiController.php +++ b/app/Http/Controllers/Api/V2/ApiController.php @@ -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]); - } } diff --git a/app/Http/Controllers/Api/V2/ArcadiaController.php b/app/Http/Controllers/Api/V2/ArcadiaController.php index aaa285d..67a980e 100644 --- a/app/Http/Controllers/Api/V2/ArcadiaController.php +++ b/app/Http/Controllers/Api/V2/ArcadiaController.php @@ -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(); } } diff --git a/app/Http/Controllers/Api/V2/CustomerController.php b/app/Http/Controllers/Api/V2/CustomerController.php index 1c00810..c94b515 100644 --- a/app/Http/Controllers/Api/V2/CustomerController.php +++ b/app/Http/Controllers/Api/V2/CustomerController.php @@ -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(); } } diff --git a/app/Http/Controllers/Api/V2/OptinController.php b/app/Http/Controllers/Api/V2/OptinController.php index 37014ee..277f4df 100644 --- a/app/Http/Controllers/Api/V2/OptinController.php +++ b/app/Http/Controllers/Api/V2/OptinController.php @@ -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(); } } diff --git a/app/Http/Controllers/Api/V2/QuestionController.php b/app/Http/Controllers/Api/V2/QuestionController.php index d38ced5..ac9782f 100644 --- a/app/Http/Controllers/Api/V2/QuestionController.php +++ b/app/Http/Controllers/Api/V2/QuestionController.php @@ -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) { diff --git a/app/Http/Controllers/Api/V2/StatsController.php b/app/Http/Controllers/Api/V2/StatsController.php index 4c97c7f..ba15892 100644 --- a/app/Http/Controllers/Api/V2/StatsController.php +++ b/app/Http/Controllers/Api/V2/StatsController.php @@ -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')) diff --git a/app/Http/Controllers/BrandController.php b/app/Http/Controllers/BrandController.php index 0be8700..49c3e1e 100644 --- a/app/Http/Controllers/BrandController.php +++ b/app/Http/Controllers/BrandController.php @@ -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 */ diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index ce1176d..6fc85d3 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -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; diff --git a/app/Http/Controllers/CustomerController.php b/app/Http/Controllers/CustomerController.php index 608b675..434dc80 100644 --- a/app/Http/Controllers/CustomerController.php +++ b/app/Http/Controllers/CustomerController.php @@ -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; diff --git a/app/Http/Controllers/LoginController.php b/app/Http/Controllers/LoginController.php index c1e18aa..aa8fd07 100644 --- a/app/Http/Controllers/LoginController.php +++ b/app/Http/Controllers/LoginController.php @@ -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(); diff --git a/app/Http/Controllers/QuestionController.php b/app/Http/Controllers/QuestionController.php index 2d0284c..def4f7d 100644 --- a/app/Http/Controllers/QuestionController.php +++ b/app/Http/Controllers/QuestionController.php @@ -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; diff --git a/app/Http/Controllers/ServiceController.php b/app/Http/Controllers/ServiceController.php index 98cdcff..ad9aa33 100644 --- a/app/Http/Controllers/ServiceController.php +++ b/app/Http/Controllers/ServiceController.php @@ -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'"); } } diff --git a/app/Http/Controllers/SingleOptinController.php b/app/Http/Controllers/SingleOptinController.php index 7174356..6ebfb7c 100644 --- a/app/Http/Controllers/SingleOptinController.php +++ b/app/Http/Controllers/SingleOptinController.php @@ -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; - } } diff --git a/app/Http/Controllers/StatsController.php b/app/Http/Controllers/StatsController.php index 1004e4d..beee43f 100644 --- a/app/Http/Controllers/StatsController.php +++ b/app/Http/Controllers/StatsController.php @@ -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')) diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index ccf3d9e..6ec696a 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -6,7 +6,6 @@ use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { - /** * The application's global HTTP middleware stack. * diff --git a/app/Http/Middleware/ApiCheckTimeStamps.php b/app/Http/Middleware/ApiCheckTimeStamps.php index 09d61fc..bce2c04 100644 --- a/app/Http/Middleware/ApiCheckTimeStamps.php +++ b/app/Http/Middleware/ApiCheckTimeStamps.php @@ -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); } } diff --git a/app/Http/Middleware/ApiResponse.php b/app/Http/Middleware/ApiResponse.php index 25ad814..d8169bc 100644 --- a/app/Http/Middleware/ApiResponse.php +++ b/app/Http/Middleware/ApiResponse.php @@ -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)); } diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 6832d89..73decb9 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -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) { diff --git a/app/Http/Middleware/CheckBrand.php b/app/Http/Middleware/CheckBrand.php index e0276f8..54ae3fb 100644 --- a/app/Http/Middleware/CheckBrand.php +++ b/app/Http/Middleware/CheckBrand.php @@ -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); } } diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php index 5cf30b3..07cc429 100644 --- a/app/Http/Middleware/EncryptCookies.php +++ b/app/Http/Middleware/EncryptCookies.php @@ -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 = []; } diff --git a/app/Http/Middleware/JsonOnly.php b/app/Http/Middleware/JsonOnly.php index 2930d72..e747db5 100644 --- a/app/Http/Middleware/JsonOnly.php +++ b/app/Http/Middleware/JsonOnly.php @@ -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); diff --git a/app/Http/Middleware/PreventRequestsDuringMaintenance.php b/app/Http/Middleware/PreventRequestsDuringMaintenance.php index 2a8072e..467b7e2 100644 --- a/app/Http/Middleware/PreventRequestsDuringMaintenance.php +++ b/app/Http/Middleware/PreventRequestsDuringMaintenance.php @@ -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 = []; } diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index 362b48b..1d80bf3 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -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) diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php index 7eb899a..5a50e7b 100644 --- a/app/Http/Middleware/TrimStrings.php +++ b/app/Http/Middleware/TrimStrings.php @@ -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. * diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php index b0550cf..7942640 100644 --- a/app/Http/Middleware/TrustHosts.php +++ b/app/Http/Middleware/TrustHosts.php @@ -4,6 +4,9 @@ namespace App\Http\Middleware; use Illuminate\Http\Middleware\TrustHosts as Middleware; +/** + * @codeCoverageIgnore + */ class TrustHosts extends Middleware { /** diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index 118a36b..3dc2af6 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -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 | diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 680465e..9d55408 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -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 = []; } diff --git a/app/Http/Middleware/XssSanitizer.php b/app/Http/Middleware/XssSanitizer.php index 5285138..6192424 100644 --- a/app/Http/Middleware/XssSanitizer.php +++ b/app/Http/Middleware/XssSanitizer.php @@ -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) diff --git a/app/Http/Requests/Api/V2/AuthenticateRequest.php b/app/Http/Requests/Api/V2/AuthenticateRequest.php index 96146fb..88c0035 100644 --- a/app/Http/Requests/Api/V2/AuthenticateRequest.php +++ b/app/Http/Requests/Api/V2/AuthenticateRequest.php @@ -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() { diff --git a/app/Http/Requests/Api/V2/BaseApiRequest.php b/app/Http/Requests/Api/V2/BaseApiRequest.php index 851fef6..fca02c6 100644 --- a/app/Http/Requests/Api/V2/BaseApiRequest.php +++ b/app/Http/Requests/Api/V2/BaseApiRequest.php @@ -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() { diff --git a/app/Http/Requests/Api/V2/GetBrandStatsRequest.php b/app/Http/Requests/Api/V2/GetBrandStatsRequest.php index ece948e..721ece0 100644 --- a/app/Http/Requests/Api/V2/GetBrandStatsRequest.php +++ b/app/Http/Requests/Api/V2/GetBrandStatsRequest.php @@ -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' diff --git a/app/Http/Requests/Api/V2/GetQuestionStatsRequest.php b/app/Http/Requests/Api/V2/GetQuestionStatsRequest.php index cca3bb3..ba56b4f 100644 --- a/app/Http/Requests/Api/V2/GetQuestionStatsRequest.php +++ b/app/Http/Requests/Api/V2/GetQuestionStatsRequest.php @@ -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', ]; diff --git a/app/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php b/app/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php index 0d1a298..e63723e 100644 --- a/app/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php +++ b/app/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php @@ -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 [ diff --git a/app/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php b/app/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php index 88728fd..40e7a26 100644 --- a/app/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php +++ b/app/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php @@ -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 [ diff --git a/app/Http/Requests/Api/V2/SingleOptinRequest.php b/app/Http/Requests/Api/V2/SingleOptinRequest.php index 9a2fda2..3a3ea0f 100644 --- a/app/Http/Requests/Api/V2/SingleOptinRequest.php +++ b/app/Http/Requests/Api/V2/SingleOptinRequest.php @@ -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 [ diff --git a/app/Http/Requests/Api/V2/StoreCustomerAnswers.php b/app/Http/Requests/Api/V2/StoreCustomerAnswers.php index 0a96d18..9e9228e 100644 --- a/app/Http/Requests/Api/V2/StoreCustomerAnswers.php +++ b/app/Http/Requests/Api/V2/StoreCustomerAnswers.php @@ -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); diff --git a/app/Http/Requests/Api/V2/StoreCustomerRequest.php b/app/Http/Requests/Api/V2/StoreCustomerRequest.php index bce514b..884601a 100644 --- a/app/Http/Requests/Api/V2/StoreCustomerRequest.php +++ b/app/Http/Requests/Api/V2/StoreCustomerRequest.php @@ -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 [ diff --git a/app/Http/Requests/Api/V2/StoreSingleOptinRequest.php b/app/Http/Requests/Api/V2/StoreSingleOptinRequest.php deleted file mode 100644 index 1da9a71..0000000 --- a/app/Http/Requests/Api/V2/StoreSingleOptinRequest.php +++ /dev/null @@ -1,13 +0,0 @@ - 'required|email:rfc,dns,spoof,filter' - ]; - } -} diff --git a/app/Http/Requests/Api/V2/UpdateCustomerRequest.php b/app/Http/Requests/Api/V2/UpdateCustomerRequest.php index 1811d1e..4b3d2b2 100644 --- a/app/Http/Requests/Api/V2/UpdateCustomerRequest.php +++ b/app/Http/Requests/Api/V2/UpdateCustomerRequest.php @@ -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 [ diff --git a/app/Http/Requests/Api/V2/UpdateSingleOptinRequest.php b/app/Http/Requests/Api/V2/UpdateSingleOptinRequest.php deleted file mode 100644 index 199954e..0000000 --- a/app/Http/Requests/Api/V2/UpdateSingleOptinRequest.php +++ /dev/null @@ -1,14 +0,0 @@ - 'required|email:rfc,dns,spoof,filter', - 'optin' => 'required|boolean' - ]; - } -} diff --git a/app/Http/Requests/CustomerExport.php b/app/Http/Requests/CustomerExport.php index 85b7c9d..8c7748a 100644 --- a/app/Http/Requests/CustomerExport.php +++ b/app/Http/Requests/CustomerExport.php @@ -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() { diff --git a/app/Http/Requests/GenerateApiToken.php b/app/Http/Requests/GenerateServiceToken.php similarity index 76% rename from app/Http/Requests/GenerateApiToken.php rename to app/Http/Requests/GenerateServiceToken.php index cecfde4..a143342 100644 --- a/app/Http/Requests/GenerateApiToken.php +++ b/app/Http/Requests/GenerateServiceToken.php @@ -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 []; } } diff --git a/app/Http/Requests/ManageBrandRequest.php b/app/Http/Requests/ManageBrandRequest.php index c12b7ea..f38264b 100644 --- a/app/Http/Requests/ManageBrandRequest.php +++ b/app/Http/Requests/ManageBrandRequest.php @@ -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() { diff --git a/app/Http/Requests/ManageServiceRequest.php b/app/Http/Requests/ManageServiceRequest.php index 250a0d1..9c51ef9 100644 --- a/app/Http/Requests/ManageServiceRequest.php +++ b/app/Http/Requests/ManageServiceRequest.php @@ -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() { diff --git a/app/Http/Requests/QuestionExport.php b/app/Http/Requests/QuestionExport.php index 8ef5ca7..4c1bb97 100644 --- a/app/Http/Requests/QuestionExport.php +++ b/app/Http/Requests/QuestionExport.php @@ -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() { diff --git a/app/Http/Requests/StatsByBrandRequest.php b/app/Http/Requests/StatsByBrandRequest.php index c5dd953..678f67b 100644 --- a/app/Http/Requests/StatsByBrandRequest.php +++ b/app/Http/Requests/StatsByBrandRequest.php @@ -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 []; } } diff --git a/app/Http/Requests/StatsByQuestionRequest.php b/app/Http/Requests/StatsByQuestionRequest.php index 75e7ebd..31865b5 100644 --- a/app/Http/Requests/StatsByQuestionRequest.php +++ b/app/Http/Requests/StatsByQuestionRequest.php @@ -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 []; } } diff --git a/app/Http/Resources/Api/V2/AnswersResource.php b/app/Http/Resources/Api/V2/AnswersResource.php index 07b32b9..c423b87 100644 --- a/app/Http/Resources/Api/V2/AnswersResource.php +++ b/app/Http/Resources/Api/V2/AnswersResource.php @@ -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) { diff --git a/app/Http/Resources/Api/V2/ApiResponse.php b/app/Http/Resources/Api/V2/ApiResponse.php index abc20dc..f75af2e 100644 --- a/app/Http/Resources/Api/V2/ApiResponse.php +++ b/app/Http/Resources/Api/V2/ApiResponse.php @@ -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 [ diff --git a/app/Http/Resources/Api/V2/BrandStatsResource.php b/app/Http/Resources/Api/V2/BrandStatsResource.php index db4ed0e..25fdf5a 100644 --- a/app/Http/Resources/Api/V2/BrandStatsResource.php +++ b/app/Http/Resources/Api/V2/BrandStatsResource.php @@ -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 [ diff --git a/app/Http/Resources/Api/V2/CustomerResource.php b/app/Http/Resources/Api/V2/CustomerResource.php index cbf066c..f0bc8c4 100644 --- a/app/Http/Resources/Api/V2/CustomerResource.php +++ b/app/Http/Resources/Api/V2/CustomerResource.php @@ -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; diff --git a/app/Http/Resources/Api/V2/QuestionResource.php b/app/Http/Resources/Api/V2/QuestionResource.php deleted file mode 100644 index 497a633..0000000 --- a/app/Http/Resources/Api/V2/QuestionResource.php +++ /dev/null @@ -1,34 +0,0 @@ -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) - ]; - } -} diff --git a/app/Http/Resources/Api/V2/QuestionStatsResource.php b/app/Http/Resources/Api/V2/QuestionStatsResource.php index 232ff52..3e6dd8b 100644 --- a/app/Http/Resources/Api/V2/QuestionStatsResource.php +++ b/app/Http/Resources/Api/V2/QuestionStatsResource.php @@ -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() ]; } diff --git a/app/Http/Resources/Api/V2/QuestionsResource.php b/app/Http/Resources/Api/V2/QuestionsResource.php index 336bf63..3f3939a 100644 --- a/app/Http/Resources/Api/V2/QuestionsResource.php +++ b/app/Http/Resources/Api/V2/QuestionsResource.php @@ -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) { diff --git a/app/Http/Resources/Api/V2/SingleOptinResource.php b/app/Http/Resources/Api/V2/SingleOptinResource.php deleted file mode 100644 index 8a05868..0000000 --- a/app/Http/Resources/Api/V2/SingleOptinResource.php +++ /dev/null @@ -1,21 +0,0 @@ - $request->email - ]; - } -} diff --git a/app/Http/Resources/CustomerResource.php b/app/Http/Resources/CustomerResource.php index 66102dd..d7d03fa 100644 --- a/app/Http/Resources/CustomerResource.php +++ b/app/Http/Resources/CustomerResource.php @@ -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, diff --git a/app/Http/Resources/StatusResource.php b/app/Http/Resources/StatusResource.php index effa264..0768686 100644 --- a/app/Http/Resources/StatusResource.php +++ b/app/Http/Resources/StatusResource.php @@ -4,6 +4,9 @@ namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; +/** + * @codeCoverageIgnore + */ class StatusResource extends JsonResource { } diff --git a/app/Http/Sanitizers/RemoveSpaces.php b/app/Http/Sanitizers/RemoveSpaces.php new file mode 100644 index 0000000..946495c --- /dev/null +++ b/app/Http/Sanitizers/RemoveSpaces.php @@ -0,0 +1,17 @@ +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 [ diff --git a/app/Jobs/StartStatJobs.php b/app/Jobs/StartStatJobs.php index 280fa7b..3f4c39c 100644 --- a/app/Jobs/StartStatJobs.php +++ b/app/Jobs/StartStatJobs.php @@ -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(); diff --git a/app/Jobs/StatJob.php b/app/Jobs/StatJob.php index 3ff9583..9984749 100644 --- a/app/Jobs/StatJob.php +++ b/app/Jobs/StatJob.php @@ -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))]; } } diff --git a/app/Jobs/UpdateCustomerLastConnection.php b/app/Jobs/UpdateCustomerLastConnection.php index ad34510..bfb22f7 100644 --- a/app/Jobs/UpdateCustomerLastConnection.php +++ b/app/Jobs/UpdateCustomerLastConnection.php @@ -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( diff --git a/app/Lib/Helper.php b/app/Lib/Helper.php index 1c39be1..849654c 100644 --- a/app/Lib/Helper.php +++ b/app/Lib/Helper.php @@ -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 } } diff --git a/app/Lib/MigrateOldData.php b/app/Lib/MigrateOldData.php index e76649f..c50dd82 100644 --- a/app/Lib/MigrateOldData.php +++ b/app/Lib/MigrateOldData.php @@ -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) { diff --git a/app/Mail/CourrierArcadiaError.php b/app/Mail/CourrierArcadiaError.php index 52a6255..b3890fc 100644 --- a/app/Mail/CourrierArcadiaError.php +++ b/app/Mail/CourrierArcadiaError.php @@ -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; diff --git a/app/Mail/ServiceCredentialRenewed.php b/app/Mail/ServiceCredentialRenewed.php index 5a32473..5516da1 100644 --- a/app/Mail/ServiceCredentialRenewed.php +++ b/app/Mail/ServiceCredentialRenewed.php @@ -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) diff --git a/app/Models/Answer.php b/app/Models/Answer.php index 6ce9028..f0616d4 100644 --- a/app/Models/Answer.php +++ b/app/Models/Answer.php @@ -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 * diff --git a/app/Models/Brand.php b/app/Models/Brand.php index 88de709..c4eb431 100644 --- a/app/Models/Brand.php +++ b/app/Models/Brand.php @@ -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(); } diff --git a/app/Models/BrandStat.php b/app/Models/BrandStat.php index b6f94da..ee3db76 100644 --- a/app/Models/BrandStat.php +++ b/app/Models/BrandStat.php @@ -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); diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 112f5d6..8fafc7d 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -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(); } diff --git a/app/Models/CustomerAnswer.php b/app/Models/CustomerAnswer.php index b613fe1..fc3be58 100644 --- a/app/Models/CustomerAnswer.php +++ b/app/Models/CustomerAnswer.php @@ -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); + } } diff --git a/app/Models/CustomerInfo.php b/app/Models/CustomerInfo.php index 3da4e03..83d375a 100644 --- a/app/Models/CustomerInfo.php +++ b/app/Models/CustomerInfo.php @@ -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 */ diff --git a/app/Models/CustomerLogin.php b/app/Models/CustomerLogin.php index 5c26542..9e504c9 100644 --- a/app/Models/CustomerLogin.php +++ b/app/Models/CustomerLogin.php @@ -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); + } } diff --git a/app/Models/CustomerOptInHistory.php b/app/Models/CustomerOptInHistory.php index 6d2ca9a..1032f18 100644 --- a/app/Models/CustomerOptInHistory.php +++ b/app/Models/CustomerOptInHistory.php @@ -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 * diff --git a/app/Models/Question.php b/app/Models/Question.php index 1b6dacd..9cfecf2 100644 --- a/app/Models/Question.php +++ b/app/Models/Question.php @@ -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 * diff --git a/app/Models/Service.php b/app/Models/Service.php index eaa5106..4607990 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -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); + } } diff --git a/app/Models/ServiceStat.php b/app/Models/ServiceStat.php index 9750b84..d246c03 100644 --- a/app/Models/ServiceStat.php +++ b/app/Models/ServiceStat.php @@ -9,12 +9,24 @@ class ServiceStat extends Model { use HasFactory; + /** + * The attributes that are mass assignable. + * + * @var string[] + */ protected $fillable = ['date']; + /** + * The model's attributes. + * + * @var array + */ protected $attributes = [ 'nb_calls' => 0 ]; + // * RELATIONS + /** * Brand BelongsTo relation * diff --git a/app/Models/SingleOptin.php b/app/Models/SingleOptin.php index ea9ede0..3f7b507 100644 --- a/app/Models/SingleOptin.php +++ b/app/Models/SingleOptin.php @@ -14,14 +14,26 @@ class SingleOptin extends Model use HasFactory; use IsCrudModel; + /** @var string */ protected $crudLabelColumn = 'email'; + /** @var string */ protected $crudValueColumn = 'id'; + /** + * The attributes that are mass assignable. + * + * @var string[] + */ protected $fillable = [ 'email' ]; + /** + * SingleOptin + * + * @param array $attributes + */ public function __construct(array $attributes = []) { self::$editableProperties = [ @@ -74,6 +86,8 @@ class SingleOptin extends Model parent::__construct($attributes); } + // * RELATIONS + /** * Brand BelongsTo relation * diff --git a/app/Policies/AnswerPolicy.php b/app/Policies/AnswerPolicy.php index 87f539f..ffb2b54 100644 --- a/app/Policies/AnswerPolicy.php +++ b/app/Policies/AnswerPolicy.php @@ -5,6 +5,9 @@ namespace App\Policies; use App\Policies\Rules\UserStaticRules as USR; use Kwaadpepper\CrudPolicies\Policies\RootPolicy; +/** + * @codeCoverageIgnore + */ class AnswerPolicy extends RootPolicy { /** diff --git a/app/Policies/BrandPolicy.php b/app/Policies/BrandPolicy.php index 46623b9..6d2d19f 100644 --- a/app/Policies/BrandPolicy.php +++ b/app/Policies/BrandPolicy.php @@ -5,6 +5,9 @@ namespace App\Policies; use App\Policies\Rules\UserStaticRules as USR; use Kwaadpepper\CrudPolicies\Policies\RootPolicy; +/** + * @codeCoverageIgnore + */ class BrandPolicy extends RootPolicy { /** diff --git a/app/Policies/CustomerPolicy.php b/app/Policies/CustomerPolicy.php index a87465f..934f90b 100644 --- a/app/Policies/CustomerPolicy.php +++ b/app/Policies/CustomerPolicy.php @@ -8,6 +8,9 @@ use App\Policies\Rules\UserStaticRules as USR; use Illuminate\Auth\Access\HandlesAuthorization; use Kwaadpepper\CrudPolicies\Policies\RootPolicy; +/** + * @codeCoverageIgnore + */ class CustomerPolicy extends RootPolicy { use HandlesAuthorization; @@ -33,6 +36,13 @@ class CustomerPolicy extends RootPolicy ]; } + /** + * Check if the customer belongs to the service + * + * @param Service $service + * @param Customer $customer + * @return boolean + */ public function customerBelongsToService(Service $service, Customer $customer) { return !\is_null($customer->login($service->brand)); diff --git a/app/Policies/QuestionPolicy.php b/app/Policies/QuestionPolicy.php index 0ed90c6..2870b43 100644 --- a/app/Policies/QuestionPolicy.php +++ b/app/Policies/QuestionPolicy.php @@ -7,6 +7,9 @@ use App\Models\Service; use App\Policies\Rules\UserStaticRules as USR; use Kwaadpepper\CrudPolicies\Policies\RootPolicy; +/** + * @codeCoverageIgnore + */ class QuestionPolicy extends RootPolicy { /** @@ -30,6 +33,13 @@ class QuestionPolicy extends RootPolicy ]; } + /** + * Check if the question belongs to the service + * + * @param Service $service + * @param Question $question + * @return boolean + */ public function questionBelongsToService(Service $service, Question $question) { return $service->brand->id === $question->brand_id; diff --git a/app/Policies/Rules/UserStaticRules.php b/app/Policies/Rules/UserStaticRules.php index 65096d8..32d42f7 100644 --- a/app/Policies/Rules/UserStaticRules.php +++ b/app/Policies/Rules/UserStaticRules.php @@ -6,14 +6,25 @@ use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules; /** * Userlevel Policiy Static Rules - *! Rules has to return boolean value + * ! Rules has to return boolean value + * @codeCoverageIgnore */ abstract class UserStaticRules extends StaticRules { + /** + * Allow everyone + * + * @return boolean + */ protected static function everyOne(): bool { return true; } + /** + * Allow no one + * + * @return boolean + */ protected static function noOne(): bool { return false; diff --git a/app/Policies/ServicePolicy.php b/app/Policies/ServicePolicy.php index 1ebf76b..0f4ec97 100644 --- a/app/Policies/ServicePolicy.php +++ b/app/Policies/ServicePolicy.php @@ -5,6 +5,9 @@ namespace App\Policies; use App\Policies\Rules\UserStaticRules as USR; use Kwaadpepper\CrudPolicies\Policies\RootPolicy; +/** + * @codeCoverageIgnore + */ class ServicePolicy extends RootPolicy { /** diff --git a/app/Policies/SingleOptinPolicy.php b/app/Policies/SingleOptinPolicy.php index 782a3ea..c7ee0f4 100644 --- a/app/Policies/SingleOptinPolicy.php +++ b/app/Policies/SingleOptinPolicy.php @@ -5,6 +5,9 @@ namespace App\Policies; use App\Policies\Rules\UserStaticRules as USR; use Kwaadpepper\CrudPolicies\Policies\RootPolicy; +/** + * @codeCoverageIgnore + */ class SingleOptinPolicy extends RootPolicy { /** diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 1970e56..8a0abe4 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -14,18 +14,11 @@ use Illuminate\Support\Facades\URL; use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; +/** + * @codeCoverageIgnore + */ class AppServiceProvider extends ServiceProvider { - /** - * Register any application services. - * - * @return void - */ - public function register() - { - // - } - /** * Bootstrap any application services. * @@ -33,20 +26,20 @@ class AppServiceProvider extends ServiceProvider */ public function boot() { - // STATS jobs Rate Limiting + // * STATS jobs Rate Limiting. RateLimiter::for('stats', function () { return Limit::perMinute(60)->by(2); }); - //! Important pourra etre retiré quand la V1 sera supprimé + // ! Important pourra etre retiré quand la V1 sera supprimé // empeche l'encapsulation dans data JsonResource::withoutWrapping(); - // LOCAL HTTPS FIX + // * LOCAL HTTPS FIX. if (!in_array(config('app.env'), ['local', 'testing'])) { URL::forceScheme('https'); } - // HA PROXY FIX + // * HA PROXY FIX. App::singleton('clientIp', function () { if ( config('app.env') === 'local' @@ -57,14 +50,14 @@ class AppServiceProvider extends ServiceProvider request()->getClientIp(); } }); - // LOCAL FORCE eg: for Carbon + // * LOCAL FORCE eg: for Carbon. setlocale(LC_ALL, \sprintf( '%s_%s.UTF-8', config('app.locale'), \strtoupper(config('app.locale')) )); Carbon::setLocale(config('app.locale')); - // FORCE BOOTSTRAP PAGINATOR + // * FORCE BOOTSTRAP PAGINATOR. if (!app()->runningInConsole()) { Paginator::useBootstrap(); } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 623b4b8..480ade9 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,8 +2,6 @@ namespace App\Providers; -use App\Enums\ApiError; -use App\Exceptions\Api\V2\ApiErrorException; use App\Lib\Helper; use App\Models\Service; use Illuminate\Database\Eloquent\ModelNotFoundException; @@ -13,7 +11,6 @@ use Illuminate\Support\Facades\Auth; class AuthServiceProvider extends ServiceProvider { - /** * The policy mappings for the application. * @@ -30,41 +27,50 @@ class AuthServiceProvider extends ServiceProvider { $this->registerPolicies(); - Auth::viaRequest('api-token', function (Request $request) { - // https://stackoverflow.com/questions/25838183/what-is-the-oauth-2-0-bearer-token-exactly/25843058 - /** - * Un token devrais être fourni pour chaque requete apres authentification - * api_token param est requis par Auth - */ - $apiToken = $request->api_token; + Auth::viaRequest('api-hmac', function (Request $request) { - if (is_null($apiToken)) { - throw new ApiErrorException(ApiError::apiAuthFailed(), 401); + /** + * Un token devrais être fourni pour chaque requete + */ + if (is_null($request->bearer_token)) { + return null; } + // * Si la route actuelle n'as pas de nom alors on refuse de la traiter. + if (!(Helper::getRoute($request))) { + // @codeCoverageIgnoreStart + return null; + // @codeCoverageIgnoreEnd + } + + $service = null; + + try { + // * On récupère le service actuel correspondant au token fourni. + $service = Service::getFromBearerToken($request->bearer_token ?? ''); + } catch (ModelNotFoundException $e) { + return null; + } + + // * Preload the brand as we will access it often later. + $service = $service->load('brand'); + /** ** All ApiV2 routes must pass Brand model to controller action since a Brand owns ** a service token, otherwise the api cannot be called. ** This hack is a custom binder instead of passing ** this to route as a parameter */ - if (!($route = Helper::getRoute($request))) { - throw new ApiErrorException(ApiError::apiAuthFailed(), 401); - } - - try { - $service = Service::getFromApiToken($apiToken); - } catch (ModelNotFoundException $e) { - throw new ApiErrorException(ApiError::apiAuthFailed(), 401); - } - $service = $service->load('brand'); app()->bind(Service::class, function () use ($service) { return $service; }); - if ($service->authenticateApi($apiToken)) { + + // * On authenticate la request en vérifiant sa signature. + if ($service->authenticateApi($request)) { return $service; } - throw new ApiErrorException(ApiError::apiAuthFailed(), 401); + + return null; }); } } diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php deleted file mode 100644 index 395c518..0000000 --- a/app/Providers/BroadcastServiceProvider.php +++ /dev/null @@ -1,21 +0,0 @@ -configureRateLimiting(); $this->routes(function () { + // @codeCoverageIgnoreStart Route::prefix('api')->middleware('api')->namespace($this->namespace)->group(base_path('routes/api.php')); Route::middleware('web')->namespace($this->namespace)->group(base_path('routes/web.php')); + // @codeCoverageIgnoreEnd }); Route::model('brand', Brand::class); @@ -46,28 +48,40 @@ class RouteServiceProvider extends ServiceProvider Route::model('single_optin', SingleOptin::class); Route::model('question', Question::class); Route::model('answer', Answer::class); + Route::model('customer', Customer::class); - Route::bind('customer', function ($value) { - if (\ctype_digit($value)) { - $customer = Customer::where('id', '=', $value)->firstOrFail(); + // * Api parameters Bind. + Route::model('serviceQuestion', Question::class); + Route::bind('serviceCustomer', function ($value) { + // * Load brand + /** @var \App\Models\Brand $brand */ + $brand = app(Service::class)->brand; + + // ? Parameter is email + if (strpos($value, '@') !== false) { + $customer = Customer::whereHas('brands', function ($q) use ($brand) { + $q->where('brands.id', '=', $brand->id); + })->where('email', '=', $value)->firstOrFail(); } - if (!isset($customer) and strpos($value, '@') !== false) { - $customer = Customer::where('email', '=', $value)->firstOrFail(); - } - if (!isset($customer)) { - try { - $customer = CustomerInfo::with('customer') - ->where('hashcode', $value)->firstOrFail()->customer; - } catch (ModelNotFoundException $e) { - // Uniform Not found exception using model Customer - throw (new ModelNotFoundException())->setModel(Customer::class); + + try { + // ? Have we found a customer yet + if (!isset($customer) or \get_class($customer) !== Customer::class) { + // ? Parameter is hashcode + $customer = CustomerInfo::whereHas('brand', function ($q) use ($brand) { + $q->where('brands.id', '=', $brand->id); + })->where('hashcode', $value) + ->with('customer')->firstOrFail()->customer; } - } - if ($brand = app('App\Models\Service')->brand) { + + // * Load Customer Login and Info for this brand. /** @var \App\Models\Customer $customer */ - $customer = $customer->loadBrandData($brand); + + return $customer->loadBrandData($brand); + } catch (ModelNotFoundException $e) { + // * Uniform Not found exception using model Customer. + throw (new ModelNotFoundException())->setModel(Customer::class); } - return $customer; }); } @@ -78,9 +92,16 @@ class RouteServiceProvider extends ServiceProvider */ protected function configureRateLimiting() { + RateLimiter::for('web', function (Request $request) { + // @codeCoverageIgnoreStart + return Limit::perMinute(25) + ->by(optional($request->user())->id ?: app('clientIp')); + // @codeCoverageIgnoreEnd + }); + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass RateLimiter::for('api', function (Request $request) { - return Limit::perMinute(config('api.apiRequestRatePerMinute')) - ->by(optional($request->user())->id ?: $request->ip()); + return Limit::perMinute(config('api.apiRequestRatePerMinute', 100)) + ->by(app('clientIp')); }); } } diff --git a/clients/V2 b/clients/V2 index f1398e1..f364478 160000 --- a/clients/V2 +++ b/clients/V2 @@ -1 +1 @@ -Subproject commit f1398e1e1405ea1eba73aa3885e9f96de872954c +Subproject commit f36447854b6d3d99af5188dc7fddf14a135f586c diff --git a/composer.json b/composer.json index fa124ee..35b45a0 100644 --- a/composer.json +++ b/composer.json @@ -1,110 +1,117 @@ { - "name": "ldc/ldccrmv2client", - "type": "project", - "version": "2.3.0", - "description": "LDC CRM V2", - "authors": [ - { - "name": "Romain Bonhomme", - "email": "romain.bonhomme@ldc.fr" - }, - { - "name": "Vincent Bridier", - "email": "vicent.bridier@ldc.fr", - "homepage": "https://bridier.me" - }, - { - "name": "Jérémy Munsch", - "email": "jeremy.munsch@ldc.fr", - "homepage": "https://jeremydev.ovh" - } - ], - "license": "MIT", - "require": { - "php": "^7", - "ext-curl": "*", - "ext-dom": "*", - "ext-json": "*", - "ext-memcached": "^3.1", - "arondeparon/laravel-request-sanitizer": "^3.1", - "axlon/laravel-postal-code-validation": "^3.2", - "cache/memcached-adapter": "^1.1", - "doctrine/dbal": "^3.0", - "fideloper/proxy": "^4.4", - "fruitcake/laravel-cors": "^2.0", - "guzzlehttp/guzzle": "^7.0.1", - "kwaadpepper/crud-policies": "^0.1.22", - "kwaadpepper/enum": "^2.1", - "kwaadpepper/exception-handler": "^0.0.5", - "laravel-lang/lang": "~7.0", - "laravel/framework": "^8.39", - "laravel/tinker": "^2.5", - "league/csv": "^9.7", - "propaganistas/laravel-phone": "^4.3", - "rennokki/laravel-eloquent-query-cache": "^3.0", - "spatie/laravel-mix-preload": "^1.2" - }, - "require-dev": { - "facade/ignition": "^2.5", - "fakerphp/faker": "^1.9.1", - "mockery/mockery": "^1.4.2", - "nunomaduro/collision": "^5.0", - "phpunit/phpunit": "^9.3.3", - "respect/validation": "^2.2", - "slevomat/coding-standard": "^7.0", - "squizlabs/php_codesniffer": "^3.5", - "symfony/process": "^5.3" - }, - "config": { - "optimize-autoloader": true, - "preferred-install": "dist", - "sort-packages": true, - "platform": { - "php": "7.3.26" - } - }, - "extra": { - "laravel": { - "dont-discover": [] - } - }, - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Factories\\": "database/factories/", - "Database\\migrations\\": "database/migrations/", - "Database\\Seeders\\": "database/seeders/", - "LDC\\LDCCRM\\" : "clients/V2/src/", - "LDCCRM\\" : "clients/" - } - }, - "autoload-dev": { - "psr-4": { - "Tests\\": "tests/" - } - }, - "minimum-stability": "dev", - "prefer-stable": true, - "scripts": { + "name": "ldc/ldccrmv2client", + "type": "project", + "version": "3.0.0", + "description": "LDC CRM V2 - revamped", + "authors": [ + { + "name": "Romain Bonhomme", + "email": "romain.bonhomme@ldc.fr" + }, + { + "name": "Vincent Bridier", + "email": "vicent.bridier@ldc.fr", + "homepage": "https://bridier.me" + }, + { + "name": "Jérémy Munsch", + "email": "jeremy.munsch@ldc.fr", + "homepage": "https://jeremydev.ovh" + } + ], + "license": "MIT", + "require": { + "php": "^7", + "ext-curl": "*", + "ext-dom": "*", + "ext-json": "*", + "arondeparon/laravel-request-sanitizer": "^3.1", + "axlon/laravel-postal-code-validation": "^3.2", + "doctrine/dbal": "^3.0", + "fideloper/proxy": "^4.4", + "fruitcake/laravel-cors": "^2.0", + "guzzlehttp/guzzle": "^7.0.1", + "kwaadpepper/crud-policies": "^0.1.22", + "kwaadpepper/enum": "^2.1", + "kwaadpepper/exception-handler": "^0.0.6", + "laravel-lang/lang": "~7.0", + "laravel/framework": "^8.39", + "laravel/tinker": "^2.5", + "league/csv": "^9.7", + "propaganistas/laravel-phone": "^4.3", + "rennokki/laravel-eloquent-query-cache": "^3.0", + "spatie/laravel-mix-preload": "^1.2" + }, + "require-dev": { + "brianium/paratest": "^6.4", + "facade/ignition": "^2.5", + "fakerphp/faker": "^1.9.1", + "mockery/mockery": "^1.4.2", + "nunomaduro/collision": "^5.0", + "phpunit/phpunit": "^9.3.3", + "respect/validation": "^2.2", + "slevomat/coding-standard": "^7.0", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^5.3" + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "platform": { + "php": "7.3.26" + } + }, + "extra": { + "laravel": { + "dont-discover": [] + } + }, + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Factories\\": "database/factories/", + "Database\\migrations\\": "database/migrations/", + "Database\\Seeders\\": "database/seeders/", + "LDC\\LDCCRM\\": "clients/V2/src/", + "LDCCRM\\": "clients/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "minimum-stability": "dev", + "prefer-stable": true, + "scripts": { "test": [ - "echo 'check memcached is running';netcat -z 127.0.0.1 11211", + "@exportclear", "@configclear", "./test.sh" ], - "configclear": "@php artisan config:clear", - "runtestmigration": "@php artisan migrate:fresh --env=testing --seed", - "runtest": "@php artisan test", - "runserve": "@php artisan serve --host=127.0.0.1 --port=8888 --env=testing&", - "killserve": "kill $(ps -f | grep '[1]27.0.0.1:8888' | awk '{print $2}')", - "post-autoload-dump": [ - "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", - "@php artisan package:discover --ansi" - ], - "post-root-package-install": [ - "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" - ], - "post-create-project-cmd": [ - "@php artisan key:generate --ansi" - ] - } + "testCoverage": [ + "@exportclear", + "@configclear", + "./test.sh -c" + ], + "stressTest": "@php artisan test -p --testsuite StressTest", + "exportclear": "rm -f storage/export/arcadia/*.xml 2> /dev/null", + "configclear": "@php artisan config:clear", + "runtestmigration": "@php artisan migrate:fresh --env=testing --seed", + "runtest": "@php artisan test --no-coverage --testsuite CRM,Client", + "runtestWithCoverage": "@php artisan test --testsuite CRM,Client", + "runserve": "@php artisan serve --host=127.0.0.1 --port=8888 --env=testing&", + "killserve": "kill $(ps -f | grep '[1]27.0.0.1:8888' | awk '{print $2}')", + "post-autoload-dump": [ + "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php artisan package:discover --ansi" + ], + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "@php artisan key:generate --ansi" + ] + } } diff --git a/composer.lock b/composer.lock index 51b586f..9473655 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d5902dd3c7b94b0d612840e1117525c8", + "content-hash": "70a1ba01f5628ab968bd053cd4d9aaad", "packages": [ { "name": "arondeparon/laravel-request-sanitizer", @@ -253,269 +253,6 @@ ], "time": "2021-08-15T20:50:18+00:00" }, - { - "name": "cache/adapter-common", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/php-cache/adapter-common.git", - "reference": "6b87c5cbdf03be42437b595dbe5de8e97cd1d497" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-cache/adapter-common/zipball/6b87c5cbdf03be42437b595dbe5de8e97cd1d497", - "reference": "6b87c5cbdf03be42437b595dbe5de8e97cd1d497", - "shasum": "" - }, - "require": { - "cache/tag-interop": "^1.0", - "php": "^5.6 || ^7.0 || ^8.0", - "psr/cache": "^1.0", - "psr/log": "^1.0", - "psr/simple-cache": "^1.0" - }, - "require-dev": { - "cache/integration-tests": "^0.16", - "phpunit/phpunit": "^5.7.21" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Cache\\Adapter\\Common\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Scherer", - "email": "aequasi@gmail.com", - "homepage": "https://github.com/aequasi" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" - } - ], - "description": "Common classes for PSR-6 adapters", - "homepage": "http://www.php-cache.com/en/latest/", - "keywords": [ - "cache", - "psr-6", - "tag" - ], - "support": { - "source": "https://github.com/php-cache/adapter-common/tree/1.2.0" - }, - "time": "2020-12-14T12:17:39+00:00" - }, - { - "name": "cache/hierarchical-cache", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-cache/hierarchical-cache.git", - "reference": "ba3746c65461b17154fb855068403670fd7fa2d3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-cache/hierarchical-cache/zipball/ba3746c65461b17154fb855068403670fd7fa2d3", - "reference": "ba3746c65461b17154fb855068403670fd7fa2d3", - "shasum": "" - }, - "require": { - "cache/adapter-common": "^1.0", - "php": "^5.6 || ^7.0 || ^8.0", - "psr/cache": "^1.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.21" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Cache\\Hierarchy\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Scherer", - "email": "aequasi@gmail.com", - "homepage": "https://github.com/aequasi" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" - } - ], - "description": "A helper trait and interface to your PSR-6 cache to support hierarchical keys.", - "homepage": "http://www.php-cache.com/en/latest/", - "keywords": [ - "cache", - "hierarchical", - "hierarchy", - "psr-6" - ], - "support": { - "source": "https://github.com/php-cache/hierarchical-cache/tree/1.1.0" - }, - "time": "2020-12-14T12:17:39+00:00" - }, - { - "name": "cache/memcached-adapter", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-cache/memcached-adapter.git", - "reference": "3e2105684bc2813646c2f44ce9c403020ff2145f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-cache/memcached-adapter/zipball/3e2105684bc2813646c2f44ce9c403020ff2145f", - "reference": "3e2105684bc2813646c2f44ce9c403020ff2145f", - "shasum": "" - }, - "require": { - "cache/adapter-common": "^1.0", - "cache/hierarchical-cache": "^1.0", - "php": "^5.6 || ^7.0 || ^8.0", - "psr/cache": "^1.0", - "psr/simple-cache": "^1.0" - }, - "provide": { - "psr/cache-implementation": "^1.0", - "psr/simple-cache-implementation": "^1.0" - }, - "require-dev": { - "cache/integration-tests": "^0.16", - "phpunit/phpunit": "^5.7.21" - }, - "suggest": { - "ext-memcached": "The extension required to use this pool." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Cache\\Adapter\\Memcached\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Scherer", - "email": "aequasi@gmail.com", - "homepage": "https://github.com/aequasi" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" - } - ], - "description": "A PSR-6 cache implementation using Memcached. This implementation supports tags", - "homepage": "http://www.php-cache.com/en/latest/", - "keywords": [ - "cache", - "memcached", - "psr-6", - "tag" - ], - "support": { - "source": "https://github.com/php-cache/memcached-adapter/tree/1.1.0" - }, - "time": "2020-12-14T12:17:39+00:00" - }, - { - "name": "cache/tag-interop", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-cache/tag-interop.git", - "reference": "909a5df87e698f1665724a9e84851c11c45fbfb9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-cache/tag-interop/zipball/909a5df87e698f1665724a9e84851c11c45fbfb9", - "reference": "909a5df87e698f1665724a9e84851c11c45fbfb9", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0 || ^8.0", - "psr/cache": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Cache\\TagInterop\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com", - "homepage": "https://github.com/nicolas-grekas" - } - ], - "description": "Framework interoperable interfaces for tags", - "homepage": "https://www.php-cache.com/en/latest/", - "keywords": [ - "cache", - "psr", - "psr6", - "tag" - ], - "support": { - "issues": "https://github.com/php-cache/tag-interop/issues", - "source": "https://github.com/php-cache/tag-interop/tree/1.0.1" - }, - "time": "2020-12-04T14:11:04+00:00" - }, { "name": "composer/package-versions-deprecated", "version": "1.11.99.4", @@ -690,36 +427,38 @@ }, { "name": "doctrine/dbal", - "version": "3.1.3", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8" + "reference": "5d54f63541d7bed1156cb5c9b79274ced61890e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/96b0053775a544b4a6ab47654dac0621be8b4cf8", - "reference": "96b0053775a544b4a6ab47654dac0621be8b4cf8", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/5d54f63541d7bed1156cb5c9b79274ced61890e4", + "reference": "5d54f63541d7bed1156cb5c9b79274ced61890e4", "shasum": "" }, "require": { "composer/package-versions-deprecated": "^1.11.99", - "doctrine/cache": "^1.0|^2.0", + "doctrine/cache": "^1.11|^2.0", "doctrine/deprecations": "^0.5.3", "doctrine/event-manager": "^1.0", - "php": "^7.3 || ^8.0" + "php": "^7.3 || ^8.0", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" }, "require-dev": { "doctrine/coding-standard": "9.0.0", "jetbrains/phpstorm-stubs": "2021.1", - "phpstan/phpstan": "0.12.99", - "phpstan/phpstan-strict-rules": "^0.12.11", + "phpstan/phpstan": "1.2.0", + "phpstan/phpstan-strict-rules": "^1.1", "phpunit/phpunit": "9.5.10", "psalm/plugin-phpunit": "0.16.1", - "squizlabs/php_codesniffer": "3.6.0", + "squizlabs/php_codesniffer": "3.6.1", "symfony/cache": "^5.2|^6.0", "symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0", - "vimeo/psalm": "4.10.0" + "vimeo/psalm": "4.13.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -779,7 +518,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.1.3" + "source": "https://github.com/doctrine/dbal/tree/3.2.0" }, "funding": [ { @@ -795,7 +534,7 @@ "type": "tidelift" } ], - "time": "2021-10-02T16:15:05+00:00" + "time": "2021-11-26T21:00:12+00:00" }, { "name": "doctrine/deprecations", @@ -1489,16 +1228,16 @@ }, { "name": "giggsey/libphonenumber-for-php", - "version": "8.12.36", + "version": "8.12.39", "source": { "type": "git", "url": "https://github.com/giggsey/libphonenumber-for-php.git", - "reference": "385660e434e7ad4bf023dde39302e561199b9bb4" + "reference": "6ae517989e96f069141ffeb56ccbbd849520f6ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/385660e434e7ad4bf023dde39302e561199b9bb4", - "reference": "385660e434e7ad4bf023dde39302e561199b9bb4", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/6ae517989e96f069141ffeb56ccbbd849520f6ff", + "reference": "6ae517989e96f069141ffeb56ccbbd849520f6ff", "shasum": "" }, "require": { @@ -1558,7 +1297,7 @@ "issues": "https://github.com/giggsey/libphonenumber-for-php/issues", "source": "https://github.com/giggsey/libphonenumber-for-php" }, - "time": "2021-10-26T09:45:49+00:00" + "time": "2021-12-07T08:51:54+00:00" }, { "name": "giggsey/locale", @@ -1616,16 +1355,16 @@ }, { "name": "graham-campbell/result-type", - "version": "v1.0.3", + "version": "v1.0.4", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "296c015dc30ec4322168c5ad3ee5cc11dae827ac" + "reference": "0690bde05318336c7221785f2a932467f98b64ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/296c015dc30ec4322168c5ad3ee5cc11dae827ac", - "reference": "296c015dc30ec4322168c5ad3ee5cc11dae827ac", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca", + "reference": "0690bde05318336c7221785f2a932467f98b64ca", "shasum": "" }, "require": { @@ -1648,7 +1387,8 @@ "authors": [ { "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk" + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" } ], "description": "An Implementation Of The Result Type", @@ -1661,7 +1401,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.3" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4" }, "funding": [ { @@ -1673,20 +1413,20 @@ "type": "tidelift" } ], - "time": "2021-10-17T19:48:54+00:00" + "time": "2021-11-21T21:41:47+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.4.0", + "version": "7.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94" + "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/868b3571a039f0ebc11ac8f344f4080babe2cb94", - "reference": "868b3571a039f0ebc11ac8f344f4080babe2cb94", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee0a041b1760e6a53d2a39c8c34115adc2af2c79", + "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79", "shasum": "" }, "require": { @@ -1695,7 +1435,7 @@ "guzzlehttp/psr7": "^1.8.3 || ^2.1", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2" + "symfony/deprecation-contracts": "^2.2 || ^3.0" }, "provide": { "psr/http-client-implementation": "1.0" @@ -1781,7 +1521,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.4.0" + "source": "https://github.com/guzzle/guzzle/tree/7.4.1" }, "funding": [ { @@ -1797,7 +1537,7 @@ "type": "tidelift" } ], - "time": "2021-10-18T09:52:00+00:00" + "time": "2021-12-06T18:43:05+00:00" }, { "name": "guzzlehttp/promises", @@ -2064,16 +1804,16 @@ }, { "name": "kwaadpepper/enum", - "version": "2.1.1", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/Kwaadpepper/enum.git", - "reference": "1c342113b63d15c8b30e5d8a0af5d4d750392037" + "reference": "bbd08723895cad5a41bc0d57e565fe64fa13ca41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Kwaadpepper/enum/zipball/1c342113b63d15c8b30e5d8a0af5d4d750392037", - "reference": "1c342113b63d15c8b30e5d8a0af5d4d750392037", + "url": "https://api.github.com/repos/Kwaadpepper/enum/zipball/bbd08723895cad5a41bc0d57e565fe64fa13ca41", + "reference": "bbd08723895cad5a41bc0d57e565fe64fa13ca41", "shasum": "" }, "require": { @@ -2118,22 +1858,22 @@ ], "support": { "issues": "https://github.com/Kwaadpepper/enum/issues", - "source": "https://github.com/Kwaadpepper/enum/tree/2.1.1" + "source": "https://github.com/Kwaadpepper/enum/tree/2.1.2" }, - "time": "2021-09-20T20:37:21+00:00" + "time": "2021-11-18T16:52:08+00:00" }, { "name": "kwaadpepper/exception-handler", - "version": "0.0.5", + "version": "0.0.6", "source": { "type": "git", "url": "https://github.com/Kwaadpepper/exception-handler.git", - "reference": "52a3f5a3f72246f6d99a486cf2951c5c65b88d1a" + "reference": "9e17a9c0a43755d91cb98717e3fc7ba17db0c1c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Kwaadpepper/exception-handler/zipball/52a3f5a3f72246f6d99a486cf2951c5c65b88d1a", - "reference": "52a3f5a3f72246f6d99a486cf2951c5c65b88d1a", + "url": "https://api.github.com/repos/Kwaadpepper/exception-handler/zipball/9e17a9c0a43755d91cb98717e3fc7ba17db0c1c7", + "reference": "9e17a9c0a43755d91cb98717e3fc7ba17db0c1c7", "shasum": "" }, "require": { @@ -2179,9 +1919,9 @@ ], "support": { "issues": "https://github.com/Kwaadpepper/exception-handler/issues", - "source": "https://github.com/Kwaadpepper/exception-handler/tree/0.0.5" + "source": "https://github.com/Kwaadpepper/exception-handler/tree/0.0.6" }, - "time": "2021-11-08T15:35:37+00:00" + "time": "2021-12-01T08:54:11+00:00" }, { "name": "laravel-lang/lang", @@ -2230,16 +1970,16 @@ }, { "name": "laravel/framework", - "version": "v8.69.0", + "version": "v8.76.2", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "545181da688db64fed6d8427e55f630a90ca0d32" + "reference": "c67acfdc968f487b6235435080eef62a7e2ed055" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/545181da688db64fed6d8427e55f630a90ca0d32", - "reference": "545181da688db64fed6d8427e55f630a90ca0d32", + "url": "https://api.github.com/repos/laravel/framework/zipball/c67acfdc968f487b6235435080eef62a7e2ed055", + "reference": "c67acfdc968f487b6235435080eef62a7e2ed055", "shasum": "" }, "require": { @@ -2261,15 +2001,15 @@ "psr/simple-cache": "^1.0", "ramsey/uuid": "^4.2.2", "swiftmailer/swiftmailer": "^6.3", - "symfony/console": "^5.1.4", - "symfony/error-handler": "^5.1.4", - "symfony/finder": "^5.1.4", - "symfony/http-foundation": "^5.1.4", - "symfony/http-kernel": "^5.1.4", - "symfony/mime": "^5.1.4", - "symfony/process": "^5.1.4", - "symfony/routing": "^5.1.4", - "symfony/var-dumper": "^5.1.4", + "symfony/console": "^5.4", + "symfony/error-handler": "^5.4", + "symfony/finder": "^5.4", + "symfony/http-foundation": "^5.4", + "symfony/http-kernel": "^5.4", + "symfony/mime": "^5.4", + "symfony/process": "^5.4", + "symfony/routing": "^5.4", + "symfony/var-dumper": "^5.4", "tijsverkoyen/css-to-inline-styles": "^2.2.2", "vlucas/phpdotenv": "^5.2", "voku/portable-ascii": "^1.4.8" @@ -2316,21 +2056,21 @@ }, "require-dev": { "aws/aws-sdk-php": "^3.198.1", - "doctrine/dbal": "^2.13.3|^3.1.2", + "doctrine/dbal": "^2.13.3|^3.1.4", "filp/whoops": "^2.14.3", "guzzlehttp/guzzle": "^6.5.5|^7.0.1", "league/flysystem-cached-adapter": "^1.0", "mockery/mockery": "^1.4.4", - "orchestra/testbench-core": "^6.23", + "orchestra/testbench-core": "^6.27", "pda/pheanstalk": "^4.0", "phpunit/phpunit": "^8.5.19|^9.5.8", "predis/predis": "^1.1.9", - "symfony/cache": "^5.1.4" + "symfony/cache": "^5.4" }, "suggest": { "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.198.1).", "brianium/paratest": "Required to run tests in parallel (^6.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.2).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).", "ext-bcmath": "Required to use the multiple_of validation rule.", "ext-ftp": "Required to use the Flysystem FTP driver.", "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", @@ -2351,9 +2091,9 @@ "phpunit/phpunit": "Required to use assertions and run tests (^8.5.19|^9.5.8).", "predis/predis": "Required to use the predis connector (^1.1.9).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0|^6.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^5.1.4).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^5.1.4).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0|^6.0|^7.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^5.4).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^5.4).", "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).", "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)." }, @@ -2398,20 +2138,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2021-11-02T13:53:22+00:00" + "time": "2021-12-15T14:02:14+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.0.3", + "version": "v1.0.5", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "6cfc678735f22ccedad761b8cae2bab14c3d8e5b" + "reference": "25de3be1bca1b17d52ff0dc02b646c667ac7266c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/6cfc678735f22ccedad761b8cae2bab14c3d8e5b", - "reference": "6cfc678735f22ccedad761b8cae2bab14c3d8e5b", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/25de3be1bca1b17d52ff0dc02b646c667ac7266c", + "reference": "25de3be1bca1b17d52ff0dc02b646c667ac7266c", "shasum": "" }, "require": { @@ -2457,20 +2197,20 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2021-10-07T14:00:57+00:00" + "time": "2021-11-30T15:53:04+00:00" }, { "name": "laravel/tinker", - "version": "v2.6.2", + "version": "v2.6.3", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "c808a7227f97ecfd9219fbf913bad842ea854ddc" + "reference": "a9ddee4761ec8453c584e393b393caff189a3e42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/c808a7227f97ecfd9219fbf913bad842ea854ddc", - "reference": "c808a7227f97ecfd9219fbf913bad842ea854ddc", + "url": "https://api.github.com/repos/laravel/tinker/zipball/a9ddee4761ec8453c584e393b393caff189a3e42", + "reference": "a9ddee4761ec8453c584e393b393caff189a3e42", "shasum": "" }, "require": { @@ -2523,9 +2263,9 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.6.2" + "source": "https://github.com/laravel/tinker/tree/v2.6.3" }, - "time": "2021-09-28T15:47:34+00:00" + "time": "2021-12-07T16:41:42+00:00" }, { "name": "league/commonmark", @@ -2630,16 +2370,16 @@ }, { "name": "league/csv", - "version": "9.7.2", + "version": "9.7.4", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "8544655c460fd01eed0ad258e514488d4b388645" + "reference": "002f55f649e7511710dc7154ff44c7be32c8195c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/8544655c460fd01eed0ad258e514488d4b388645", - "reference": "8544655c460fd01eed0ad258e514488d4b388645", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/002f55f649e7511710dc7154ff44c7be32c8195c", + "reference": "002f55f649e7511710dc7154ff44c7be32c8195c", "shasum": "" }, "require": { @@ -2651,9 +2391,9 @@ "ext-curl": "*", "ext-dom": "*", "friendsofphp/php-cs-fixer": "^3.0", - "phpstan/phpstan": "^0.12.99", - "phpstan/phpstan-phpunit": "^0.12.22", - "phpstan/phpstan-strict-rules": "^0.12.11", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-phpunit": "^1.0.0", + "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5" }, "suggest": { @@ -2710,20 +2450,20 @@ "type": "github" } ], - "time": "2021-10-05T19:41:46+00:00" + "time": "2021-11-30T07:09:34+00:00" }, { "name": "league/flysystem", - "version": "1.1.5", + "version": "1.1.9", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "18634df356bfd4119fe3d6156bdb990c414c14ea" + "reference": "094defdb4a7001845300334e7c1ee2335925ef99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/18634df356bfd4119fe3d6156bdb990c414c14ea", - "reference": "18634df356bfd4119fe3d6156bdb990c414c14ea", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/094defdb4a7001845300334e7c1ee2335925ef99", + "reference": "094defdb4a7001845300334e7c1ee2335925ef99", "shasum": "" }, "require": { @@ -2796,7 +2536,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/1.1.5" + "source": "https://github.com/thephpleague/flysystem/tree/1.1.9" }, "funding": [ { @@ -2804,27 +2544,27 @@ "type": "other" } ], - "time": "2021-08-17T13:49:42+00:00" + "time": "2021-12-09T09:40:50+00:00" }, { "name": "league/iso3166", - "version": "3.0.0", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/thephpleague/iso3166.git", - "reference": "9976d382f270ad3f3df8a68719beb7a7179ffa1e" + "reference": "1a3ec7e6f1e4f16fce68dc239bafae217fbdcfef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/iso3166/zipball/9976d382f270ad3f3df8a68719beb7a7179ffa1e", - "reference": "9976d382f270ad3f3df8a68719beb7a7179ffa1e", + "url": "https://api.github.com/repos/thephpleague/iso3166/zipball/1a3ec7e6f1e4f16fce68dc239bafae217fbdcfef", + "reference": "1a3ec7e6f1e4f16fce68dc239bafae217fbdcfef", "shasum": "" }, "require": { "php": "^7.3|^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { @@ -2861,20 +2601,20 @@ "issues": "https://github.com/thephpleague/iso3166/issues", "source": "https://github.com/thephpleague/iso3166" }, - "time": "2020-12-05T06:50:42+00:00" + "time": "2021-10-22T12:47:55+00:00" }, { "name": "league/mime-type-detection", - "version": "1.8.0", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "b38b25d7b372e9fddb00335400467b223349fd7e" + "reference": "aa70e813a6ad3d1558fc927863d47309b4c23e69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/b38b25d7b372e9fddb00335400467b223349fd7e", - "reference": "b38b25d7b372e9fddb00335400467b223349fd7e", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/aa70e813a6ad3d1558fc927863d47309b4c23e69", + "reference": "aa70e813a6ad3d1558fc927863d47309b4c23e69", "shasum": "" }, "require": { @@ -2882,7 +2622,7 @@ "php": "^7.2 || ^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.18", + "friendsofphp/php-cs-fixer": "^3.2", "phpstan/phpstan": "^0.12.68", "phpunit/phpunit": "^8.5.8 || ^9.3" }, @@ -2905,7 +2645,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.8.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.9.0" }, "funding": [ { @@ -2917,7 +2657,7 @@ "type": "tidelift" } ], - "time": "2021-09-25T08:23:19+00:00" + "time": "2021-11-21T11:48:40+00:00" }, { "name": "monolog/monolog", @@ -3020,16 +2760,16 @@ }, { "name": "nesbot/carbon", - "version": "2.54.0", + "version": "2.55.2", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "eed83939f1aed3eee517d03a33f5ec587ac529b5" + "reference": "8c2a18ce3e67c34efc1b29f64fe61304368259a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/eed83939f1aed3eee517d03a33f5ec587ac529b5", - "reference": "eed83939f1aed3eee517d03a33f5ec587ac529b5", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/8c2a18ce3e67c34efc1b29f64fe61304368259a2", + "reference": "8c2a18ce3e67c34efc1b29f64fe61304368259a2", "shasum": "" }, "require": { @@ -3037,7 +2777,7 @@ "php": "^7.1.8 || ^8.0", "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php80": "^1.16", - "symfony/translation": "^3.4 || ^4.0 || ^5.0" + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, "require-dev": { "doctrine/dbal": "^2.0 || ^3.0", @@ -3098,6 +2838,7 @@ "time" ], "support": { + "docs": "https://carbon.nesbot.com/docs", "issues": "https://github.com/briannesbitt/Carbon/issues", "source": "https://github.com/briannesbitt/Carbon" }, @@ -3111,20 +2852,20 @@ "type": "tidelift" } ], - "time": "2021-11-01T21:22:20+00:00" + "time": "2021-12-03T14:59:52+00:00" }, { "name": "nikic/php-parser", - "version": "v4.13.1", + "version": "v4.13.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd" + "reference": "210577fe3cf7badcc5814d99455df46564f3c077" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/63a79e8daa781cac14e5195e63ed8ae231dd10fd", - "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077", + "reference": "210577fe3cf7badcc5814d99455df46564f3c077", "shasum": "" }, "require": { @@ -3165,9 +2906,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2" }, - "time": "2021-11-03T20:52:16+00:00" + "time": "2021-11-30T19:35:32+00:00" }, { "name": "opis/closure", @@ -3236,16 +2977,16 @@ }, { "name": "phpoption/phpoption", - "version": "1.8.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "5455cb38aed4523f99977c4a12ef19da4bfe2a28" + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/5455cb38aed4523f99977c4a12ef19da4bfe2a28", - "reference": "5455cb38aed4523f99977c4a12ef19da4bfe2a28", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", "shasum": "" }, "require": { @@ -3253,7 +2994,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.4.1", - "phpunit/phpunit": "^6.5.14 || ^7.0.20 || ^8.5.19 || ^9.5.8" + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" }, "type": "library", "extra": { @@ -3273,11 +3014,13 @@ "authors": [ { "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" }, { "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk" + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" } ], "description": "Option Type for PHP", @@ -3289,7 +3032,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.8.0" + "source": "https://github.com/schmittjoh/php-option/tree/1.8.1" }, "funding": [ { @@ -3301,20 +3044,20 @@ "type": "tidelift" } ], - "time": "2021-08-28T21:27:29+00:00" + "time": "2021-12-04T23:24:31+00:00" }, { "name": "propaganistas/laravel-phone", - "version": "4.3.2", + "version": "4.3.5", "source": { "type": "git", "url": "https://github.com/Propaganistas/Laravel-Phone.git", - "reference": "947247c7c9fc7c340eb1d12df49db40d299535c6" + "reference": "90ea4906a67ecbfd6255f9f77806401a81e2d6ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Propaganistas/Laravel-Phone/zipball/947247c7c9fc7c340eb1d12df49db40d299535c6", - "reference": "947247c7c9fc7c340eb1d12df49db40d299535c6", + "url": "https://api.github.com/repos/Propaganistas/Laravel-Phone/zipball/90ea4906a67ecbfd6255f9f77806401a81e2d6ac", + "reference": "90ea4906a67ecbfd6255f9f77806401a81e2d6ac", "shasum": "" }, "require": { @@ -3322,13 +3065,13 @@ "illuminate/contracts": "^8.9", "illuminate/support": "^8.0", "illuminate/validation": "^8.0", - "league/iso3166": "^2.0|^3.0", + "league/iso3166": "^2.0|^3.0|^4.0", "php": "^7.3|^8.0" }, "require-dev": { - "nunomaduro/larastan": "^0.6.11", + "nunomaduro/larastan": "^1.0", "orchestra/testbench": "*", - "phpunit/phpunit": "*" + "phpunit/phpunit": "^9.5.10" }, "type": "library", "extra": { @@ -3356,19 +3099,18 @@ "email": "Propaganistas@users.noreply.github.com" } ], - "description": "Adds phone number functionality to Laravel and Lumen based on Google's libphonenumber API.", + "description": "Adds phone number functionality to Laravel based on Google's libphonenumber API.", "keywords": [ "laravel", "libphonenumber", - "lumen", "phone", "validation" ], "support": { "issues": "https://github.com/Propaganistas/Laravel-Phone/issues", - "source": "https://github.com/Propaganistas/Laravel-Phone/tree/4.3.2" + "source": "https://github.com/Propaganistas/Laravel-Phone/tree/4.3.5" }, - "time": "2021-10-27T07:08:01+00:00" + "time": "2021-12-09T07:14:29+00:00" }, { "name": "psr/cache", @@ -3780,16 +3522,16 @@ }, { "name": "psy/psysh", - "version": "v0.10.9", + "version": "v0.10.12", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "01281336c4ae557fe4a994544f30d3a1bc204375" + "reference": "a0d9981aa07ecfcbea28e4bfa868031cca121e7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/01281336c4ae557fe4a994544f30d3a1bc204375", - "reference": "01281336c4ae557fe4a994544f30d3a1bc204375", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/a0d9981aa07ecfcbea28e4bfa868031cca121e7d", + "reference": "a0d9981aa07ecfcbea28e4bfa868031cca121e7d", "shasum": "" }, "require": { @@ -3849,9 +3591,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.10.9" + "source": "https://github.com/bobthecow/psysh/tree/v0.10.12" }, - "time": "2021-10-10T13:37:39+00:00" + "time": "2021-11-30T14:05:36+00:00" }, { "name": "ralouphie/getallheaders", @@ -4076,16 +3818,16 @@ }, { "name": "rennokki/laravel-eloquent-query-cache", - "version": "3.1.3", + "version": "3.1.4", "source": { "type": "git", "url": "https://github.com/renoki-co/laravel-eloquent-query-cache.git", - "reference": "ad4a9d935ddd355da9af0a086f33f2d64212da09" + "reference": "24d6aec2d2a097a64f8b44354462e3dd83c52e4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/renoki-co/laravel-eloquent-query-cache/zipball/ad4a9d935ddd355da9af0a086f33f2d64212da09", - "reference": "ad4a9d935ddd355da9af0a086f33f2d64212da09", + "url": "https://api.github.com/repos/renoki-co/laravel-eloquent-query-cache/zipball/24d6aec2d2a097a64f8b44354462e3dd83c52e4d", + "reference": "24d6aec2d2a097a64f8b44354462e3dd83c52e4d", "shasum": "" }, "require": { @@ -4097,7 +3839,7 @@ "laravel/legacy-factories": "^1.1", "mockery/mockery": "^1.4", "orchestra/database": "^5.0|^6.0", - "orchestra/testbench": "^5.0|^6.0" + "orchestra/testbench": "^6.23.1" }, "type": "library", "autoload": { @@ -4129,15 +3871,15 @@ ], "support": { "issues": "https://github.com/renoki-co/laravel-eloquent-query-cache/issues", - "source": "https://github.com/renoki-co/laravel-eloquent-query-cache/tree/3.1.3" + "source": "https://github.com/renoki-co/laravel-eloquent-query-cache/tree/3.1.4" }, "funding": [ { - "url": "https://www.patreon.com/rennokki", - "type": "patreon" + "url": "https://github.com/rennokki", + "type": "github" } ], - "time": "2021-11-05T22:03:48+00:00" + "time": "2021-12-07T14:46:30+00:00" }, { "name": "spatie/laravel-mix-preload", @@ -4280,30 +4022,31 @@ "type": "tidelift" } ], + "abandoned": "symfony/mailer", "time": "2021-10-18T15:26:12+00:00" }, { "name": "symfony/console", - "version": "v5.3.10", + "version": "v5.4.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3" + "reference": "9130e1a0fc93cb0faadca4ee917171bd2ca9e5f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3", - "reference": "d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3", + "url": "https://api.github.com/repos/symfony/console/zipball/9130e1a0fc93cb0faadca4ee917171bd2ca9e5f4", + "reference": "9130e1a0fc93cb0faadca4ee917171bd2ca9e5f4", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", + "symfony/polyfill-php73": "^1.9", "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2", - "symfony/string": "^5.1" + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" }, "conflict": { "psr/log": ">=3", @@ -4318,12 +4061,12 @@ }, "require-dev": { "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/event-dispatcher": "^4.4|^5.0", - "symfony/lock": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", - "symfony/var-dumper": "^4.4|^5.0" + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" }, "suggest": { "psr/log": "For using the console logger", @@ -4363,7 +4106,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.3.10" + "source": "https://github.com/symfony/console/tree/v5.4.1" }, "funding": [ { @@ -4379,20 +4122,20 @@ "type": "tidelift" } ], - "time": "2021-10-26T09:30:15+00:00" + "time": "2021-12-09T11:22:43+00:00" }, { "name": "symfony/css-selector", - "version": "v5.3.4", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "7fb120adc7f600a59027775b224c13a33530dd90" + "reference": "44b933f98bb4b5220d10bed9ce5662f8c2d13dcc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/7fb120adc7f600a59027775b224c13a33530dd90", - "reference": "7fb120adc7f600a59027775b224c13a33530dd90", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/44b933f98bb4b5220d10bed9ce5662f8c2d13dcc", + "reference": "44b933f98bb4b5220d10bed9ce5662f8c2d13dcc", "shasum": "" }, "require": { @@ -4429,7 +4172,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.3.4" + "source": "https://github.com/symfony/css-selector/tree/v5.4.0" }, "funding": [ { @@ -4445,20 +4188,20 @@ "type": "tidelift" } ], - "time": "2021-07-21T12:38:00+00:00" + "time": "2021-09-09T08:06:01+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", "shasum": "" }, "require": { @@ -4467,7 +4210,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -4496,7 +4239,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" }, "funding": [ { @@ -4512,32 +4255,35 @@ "type": "tidelift" } ], - "time": "2021-03-23T23:28:01+00:00" + "time": "2021-07-12T14:48:14+00:00" }, { "name": "symfony/error-handler", - "version": "v5.3.7", + "version": "v5.4.1", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "3bc60d0fba00ae8d1eaa9eb5ab11a2bbdd1fc321" + "reference": "1e3cb3565af49cd5f93e5787500134500a29f0d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/3bc60d0fba00ae8d1eaa9eb5ab11a2bbdd1fc321", - "reference": "3bc60d0fba00ae8d1eaa9eb5ab11a2bbdd1fc321", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/1e3cb3565af49cd5f93e5787500134500a29f0d9", + "reference": "1e3cb3565af49cd5f93e5787500134500a29f0d9", "shasum": "" }, "require": { "php": ">=7.2.5", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^4.4|^5.0" + "symfony/var-dumper": "^4.4|^5.0|^6.0" }, "require-dev": { - "symfony/deprecation-contracts": "^2.1", - "symfony/http-kernel": "^4.4|^5.0", - "symfony/serializer": "^4.4|^5.0" + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/http-kernel": "^4.4|^5.0|^6.0", + "symfony/serializer": "^4.4|^5.0|^6.0" }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], "type": "library", "autoload": { "psr-4": { @@ -4564,7 +4310,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v5.3.7" + "source": "https://github.com/symfony/error-handler/tree/v5.4.1" }, "funding": [ { @@ -4580,26 +4326,26 @@ "type": "tidelift" } ], - "time": "2021-08-28T15:07:08+00:00" + "time": "2021-12-01T15:04:08+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.3.7", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "ce7b20d69c66a20939d8952b617506a44d102130" + "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ce7b20d69c66a20939d8952b617506a44d102130", - "reference": "ce7b20d69c66a20939d8952b617506a44d102130", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/27d39ae126352b9fa3be5e196ccf4617897be3eb", + "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/event-dispatcher-contracts": "^2", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/event-dispatcher-contracts": "^2|^3", "symfony/polyfill-php80": "^1.16" }, "conflict": { @@ -4611,13 +4357,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/error-handler": "^4.4|^5.0", - "symfony/expression-language": "^4.4|^5.0", - "symfony/http-foundation": "^4.4|^5.0", - "symfony/service-contracts": "^1.1|^2", - "symfony/stopwatch": "^4.4|^5.0" + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^4.4|^5.0|^6.0" }, "suggest": { "symfony/dependency-injection": "", @@ -4649,7 +4395,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.3.7" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.0" }, "funding": [ { @@ -4665,20 +4411,20 @@ "type": "tidelift" } ], - "time": "2021-08-04T21:20:46+00:00" + "time": "2021-11-23T10:19:22+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11" + "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/69fee1ad2332a7cbab3aca13591953da9cdb7a11", - "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", + "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", "shasum": "" }, "require": { @@ -4691,7 +4437,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -4728,7 +4474,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.4.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.0" }, "funding": [ { @@ -4744,24 +4490,25 @@ "type": "tidelift" } ], - "time": "2021-03-23T23:28:01+00:00" + "time": "2021-07-12T14:48:14+00:00" }, { "name": "symfony/finder", - "version": "v5.3.7", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93" + "reference": "d2f29dac98e96a98be467627bd49c2efb1bc2590" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/a10000ada1e600d109a6c7632e9ac42e8bf2fb93", - "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93", + "url": "https://api.github.com/repos/symfony/finder/zipball/d2f29dac98e96a98be467627bd49c2efb1bc2590", + "reference": "d2f29dac98e96a98be467627bd49c2efb1bc2590", "shasum": "" }, "require": { "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-php80": "^1.16" }, "type": "library", @@ -4790,7 +4537,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.3.7" + "source": "https://github.com/symfony/finder/tree/v5.4.0" }, "funding": [ { @@ -4806,111 +4553,33 @@ "type": "tidelift" } ], - "time": "2021-08-04T21:20:46+00:00" - }, - { - "name": "symfony/http-client-contracts", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/7e82f6084d7cae521a75ef2cb5c9457bbda785f4", - "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4", - "shasum": "" - }, - "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/http-client-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-04-11T23:07:08+00:00" + "time": "2021-11-28T15:25:38+00:00" }, { "name": "symfony/http-foundation", - "version": "v5.3.10", + "version": "v5.4.1", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "9f34f02e8a5fdc7a56bafe011cea1ce97300e54c" + "reference": "5dad3780023a707f4c24beac7d57aead85c1ce3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9f34f02e8a5fdc7a56bafe011cea1ce97300e54c", - "reference": "9f34f02e8a5fdc7a56bafe011cea1ce97300e54c", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5dad3780023a707f4c24beac7d57aead85c1ce3c", + "reference": "5dad3780023a707f4c24beac7d57aead85c1ce3c", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.1", "symfony/polyfill-php80": "^1.16" }, "require-dev": { "predis/predis": "~1.0", - "symfony/cache": "^4.4|^5.0", - "symfony/expression-language": "^4.4|^5.0", - "symfony/mime": "^4.4|^5.0" + "symfony/cache": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/mime": "^4.4|^5.0|^6.0" }, "suggest": { "symfony/mime": "To use the file extension guesser" @@ -4941,7 +4610,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.3.10" + "source": "https://github.com/symfony/http-foundation/tree/v5.4.1" }, "funding": [ { @@ -4957,36 +4626,35 @@ "type": "tidelift" } ], - "time": "2021-10-11T15:41:55+00:00" + "time": "2021-12-09T12:46:57+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.3.10", + "version": "v5.4.1", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "703e4079920468e9522b72cf47fd76ce8d795e86" + "reference": "2bdace75c9d6a6eec7e318801b7dc87a72375052" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/703e4079920468e9522b72cf47fd76ce8d795e86", - "reference": "703e4079920468e9522b72cf47fd76ce8d795e86", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/2bdace75c9d6a6eec7e318801b7dc87a72375052", + "reference": "2bdace75c9d6a6eec7e318801b7dc87a72375052", "shasum": "" }, "require": { "php": ">=7.2.5", "psr/log": "^1|^2", - "symfony/deprecation-contracts": "^2.1", - "symfony/error-handler": "^4.4|^5.0", - "symfony/event-dispatcher": "^5.0", - "symfony/http-client-contracts": "^1.1|^2", - "symfony/http-foundation": "^5.3.7", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^5.0|^6.0", + "symfony/http-foundation": "^5.3.7|^6.0", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-php73": "^1.9", "symfony/polyfill-php80": "^1.16" }, "conflict": { - "symfony/browser-kit": "<4.4", + "symfony/browser-kit": "<5.4", "symfony/cache": "<5.0", "symfony/config": "<5.0", "symfony/console": "<4.4", @@ -5006,19 +4674,20 @@ }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^4.4|^5.0", - "symfony/config": "^5.0", - "symfony/console": "^4.4|^5.0", - "symfony/css-selector": "^4.4|^5.0", - "symfony/dependency-injection": "^5.3", - "symfony/dom-crawler": "^4.4|^5.0", - "symfony/expression-language": "^4.4|^5.0", - "symfony/finder": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", - "symfony/routing": "^4.4|^5.0", - "symfony/stopwatch": "^4.4|^5.0", - "symfony/translation": "^4.4|^5.0", - "symfony/translation-contracts": "^1.1|^2", + "symfony/browser-kit": "^5.4|^6.0", + "symfony/config": "^5.0|^6.0", + "symfony/console": "^4.4|^5.0|^6.0", + "symfony/css-selector": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^5.3|^6.0", + "symfony/dom-crawler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/http-client-contracts": "^1.1|^2|^3", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/routing": "^4.4|^5.0|^6.0", + "symfony/stopwatch": "^4.4|^5.0|^6.0", + "symfony/translation": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2|^3", "twig/twig": "^2.13|^3.0.4" }, "suggest": { @@ -5053,7 +4722,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v5.3.10" + "source": "https://github.com/symfony/http-kernel/tree/v5.4.1" }, "funding": [ { @@ -5069,25 +4738,25 @@ "type": "tidelift" } ], - "time": "2021-10-29T08:36:48+00:00" + "time": "2021-12-09T13:36:09+00:00" }, { "name": "symfony/mime", - "version": "v5.3.8", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "a756033d0a7e53db389618653ae991eba5a19a11" + "reference": "d4365000217b67c01acff407573906ff91bcfb34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/a756033d0a7e53db389618653ae991eba5a19a11", - "reference": "a756033d0a7e53db389618653ae991eba5a19a11", + "url": "https://api.github.com/repos/symfony/mime/zipball/d4365000217b67c01acff407573906ff91bcfb34", + "reference": "d4365000217b67c01acff407573906ff91bcfb34", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php80": "^1.16" @@ -5101,10 +4770,10 @@ "require-dev": { "egulias/email-validator": "^2.1.10|^3.1", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/property-access": "^4.4|^5.1", - "symfony/property-info": "^4.4|^5.1", - "symfony/serializer": "^5.2" + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/property-access": "^4.4|^5.1|^6.0", + "symfony/property-info": "^4.4|^5.1|^6.0", + "symfony/serializer": "^5.2|^6.0" }, "type": "library", "autoload": { @@ -5136,7 +4805,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v5.3.8" + "source": "https://github.com/symfony/mime/tree/v5.4.0" }, "funding": [ { @@ -5152,7 +4821,7 @@ "type": "tidelift" } ], - "time": "2021-09-10T12:30:38+00:00" + "time": "2021-11-23T10:19:22+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5964,16 +5633,16 @@ }, { "name": "symfony/process", - "version": "v5.3.7", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "38f26c7d6ed535217ea393e05634cb0b244a1967" + "reference": "5be20b3830f726e019162b26223110c8f47cf274" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/38f26c7d6ed535217ea393e05634cb0b244a1967", - "reference": "38f26c7d6ed535217ea393e05634cb0b244a1967", + "url": "https://api.github.com/repos/symfony/process/zipball/5be20b3830f726e019162b26223110c8f47cf274", + "reference": "5be20b3830f726e019162b26223110c8f47cf274", "shasum": "" }, "require": { @@ -6006,7 +5675,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.3.7" + "source": "https://github.com/symfony/process/tree/v5.4.0" }, "funding": [ { @@ -6022,25 +5691,25 @@ "type": "tidelift" } ], - "time": "2021-08-04T21:20:46+00:00" + "time": "2021-11-28T15:25:38+00:00" }, { "name": "symfony/routing", - "version": "v5.3.7", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "be865017746fe869007d94220ad3f5297951811b" + "reference": "9eeae93c32ca86746e5d38f3679e9569981038b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/be865017746fe869007d94220ad3f5297951811b", - "reference": "be865017746fe869007d94220ad3f5297951811b", + "url": "https://api.github.com/repos/symfony/routing/zipball/9eeae93c32ca86746e5d38f3679e9569981038b1", + "reference": "9eeae93c32ca86746e5d38f3679e9569981038b1", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-php80": "^1.16" }, "conflict": { @@ -6052,11 +5721,11 @@ "require-dev": { "doctrine/annotations": "^1.12", "psr/log": "^1|^2|^3", - "symfony/config": "^5.3", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/expression-language": "^4.4|^5.0", - "symfony/http-foundation": "^4.4|^5.0", - "symfony/yaml": "^4.4|^5.0" + "symfony/config": "^5.3|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/yaml": "^4.4|^5.0|^6.0" }, "suggest": { "symfony/config": "For using the all-in-one router or any loader", @@ -6096,7 +5765,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v5.3.7" + "source": "https://github.com/symfony/routing/tree/v5.4.0" }, "funding": [ { @@ -6112,25 +5781,29 @@ "type": "tidelift" } ], - "time": "2021-08-04T21:42:42+00:00" + "time": "2021-11-23T10:19:22+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb" + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", - "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", "shasum": "" }, "require": { "php": ">=7.2.5", - "psr/container": "^1.1" + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1" + }, + "conflict": { + "ext-psr": "<1.1|>=2" }, "suggest": { "symfony/service-implementation": "" @@ -6138,7 +5811,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -6175,7 +5848,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.4.0" + "source": "https://github.com/symfony/service-contracts/tree/v2.5.0" }, "funding": [ { @@ -6191,20 +5864,20 @@ "type": "tidelift" } ], - "time": "2021-04-01T10:43:52+00:00" + "time": "2021-11-04T16:48:04+00:00" }, { "name": "symfony/string", - "version": "v5.3.10", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c" + "reference": "9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c", - "reference": "d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c", + "url": "https://api.github.com/repos/symfony/string/zipball/9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d", + "reference": "9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d", "shasum": "" }, "require": { @@ -6215,11 +5888,14 @@ "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php80": "~1.15" }, + "conflict": { + "symfony/translation-contracts": ">=3.0" + }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0", - "symfony/http-client": "^4.4|^5.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0" + "symfony/var-exporter": "^4.4|^5.0|^6.0" }, "type": "library", "autoload": { @@ -6258,7 +5934,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.3.10" + "source": "https://github.com/symfony/string/tree/v5.4.0" }, "funding": [ { @@ -6274,31 +5950,32 @@ "type": "tidelift" } ], - "time": "2021-10-27T18:21:46+00:00" + "time": "2021-11-24T10:02:00+00:00" }, { "name": "symfony/translation", - "version": "v5.3.10", + "version": "v5.4.1", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "6ef197aea2ac8b9cd63e0da7522b3771714035aa" + "reference": "8c82cd35ed861236138d5ae1c78c0c7ebcd62107" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/6ef197aea2ac8b9cd63e0da7522b3771714035aa", - "reference": "6ef197aea2ac8b9cd63e0da7522b3771714035aa", + "url": "https://api.github.com/repos/symfony/translation/zipball/8c82cd35ed861236138d5ae1c78c0c7ebcd62107", + "reference": "8c82cd35ed861236138d5ae1c78c0c7ebcd62107", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php80": "^1.16", "symfony/translation-contracts": "^2.3" }, "conflict": { "symfony/config": "<4.4", + "symfony/console": "<5.3", "symfony/dependency-injection": "<5.0", "symfony/http-kernel": "<5.0", "symfony/twig-bundle": "<5.0", @@ -6309,15 +5986,16 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0", - "symfony/console": "^4.4|^5.0", - "symfony/dependency-injection": "^5.0", - "symfony/finder": "^4.4|^5.0", - "symfony/http-kernel": "^5.0", - "symfony/intl": "^4.4|^5.0", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.0|^6.0", + "symfony/intl": "^4.4|^5.0|^6.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/service-contracts": "^1.1.2|^2", - "symfony/yaml": "^4.4|^5.0" + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" }, "suggest": { "psr/log-implementation": "To use logging capability in translator", @@ -6353,7 +6031,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v5.3.10" + "source": "https://github.com/symfony/translation/tree/v5.4.1" }, "funding": [ { @@ -6369,20 +6047,20 @@ "type": "tidelift" } ], - "time": "2021-10-10T06:43:24+00:00" + "time": "2021-12-05T20:33:52+00:00" }, { "name": "symfony/translation-contracts", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "95c812666f3e91db75385749fe219c5e494c7f95" + "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/95c812666f3e91db75385749fe219c5e494c7f95", - "reference": "95c812666f3e91db75385749fe219c5e494c7f95", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/d28150f0f44ce854e942b671fc2620a98aae1b1e", + "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e", "shasum": "" }, "require": { @@ -6394,7 +6072,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -6431,7 +6109,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v2.4.0" + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.0" }, "funding": [ { @@ -6447,20 +6125,20 @@ "type": "tidelift" } ], - "time": "2021-03-23T23:28:01+00:00" + "time": "2021-08-17T14:20:01+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.3.10", + "version": "v5.4.1", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "875432adb5f5570fff21036fd22aee244636b7d1" + "reference": "2366ac8d8abe0c077844613c1a4f0c0a9f522dcc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/875432adb5f5570fff21036fd22aee244636b7d1", - "reference": "875432adb5f5570fff21036fd22aee244636b7d1", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2366ac8d8abe0c077844613c1a4f0c0a9f522dcc", + "reference": "2366ac8d8abe0c077844613c1a4f0c0a9f522dcc", "shasum": "" }, "require": { @@ -6474,8 +6152,9 @@ }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", + "symfony/console": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/uid": "^5.1|^6.0", "twig/twig": "^2.13|^3.0.4" }, "suggest": { @@ -6519,7 +6198,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.3.10" + "source": "https://github.com/symfony/var-dumper/tree/v5.4.1" }, "funding": [ { @@ -6535,30 +6214,30 @@ "type": "tidelift" } ], - "time": "2021-10-26T09:30:15+00:00" + "time": "2021-12-01T15:04:08+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "2.2.3", + "version": "2.2.4", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "b43b05cf43c1b6d849478965062b6ef73e223bb5" + "reference": "da444caae6aca7a19c0c140f68c6182e337d5b1c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/b43b05cf43c1b6d849478965062b6ef73e223bb5", - "reference": "b43b05cf43c1b6d849478965062b6ef73e223bb5", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/da444caae6aca7a19c0c140f68c6182e337d5b1c", + "reference": "da444caae6aca7a19c0c140f68c6182e337d5b1c", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "php": "^5.5 || ^7.0 || ^8.0", - "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0" + "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" }, "type": "library", "extra": { @@ -6586,22 +6265,22 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.3" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.4" }, - "time": "2020-07-13T06:12:54+00:00" + "time": "2021-12-08T09:12:39+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v5.3.1", + "version": "v5.4.1", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "accaddf133651d4b5cf81a119f25296736ffc850" + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/accaddf133651d4b5cf81a119f25296736ffc850", - "reference": "accaddf133651d4b5cf81a119f25296736ffc850", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/264dce589e7ce37a7ba99cb901eed8249fbec92f", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f", "shasum": "" }, "require": { @@ -6624,7 +6303,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3-dev" + "dev-master": "5.4-dev" } }, "autoload": { @@ -6639,11 +6318,13 @@ "authors": [ { "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk" + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" }, { "name": "Vance Lucas", - "email": "vance@vancelucas.com" + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" } ], "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", @@ -6654,7 +6335,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.3.1" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.1" }, "funding": [ { @@ -6666,7 +6347,7 @@ "type": "tidelift" } ], - "time": "2021-10-02T19:24:42+00:00" + "time": "2021-12-12T23:22:04+00:00" }, { "name": "voku/portable-ascii", @@ -6802,6 +6483,101 @@ } ], "packages-dev": [ + { + "name": "brianium/paratest", + "version": "v6.4.1", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "c32a5c4fc2ff339202437d25d19a5f496f880d61" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/c32a5c4fc2ff339202437d25d19a5f496f880d61", + "reference": "c32a5c4fc2ff339202437d25d19a5f496f880d61", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "php": "^7.3 || ^8.0", + "phpunit/php-code-coverage": "^9.2.9", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-timer": "^5.0.3", + "phpunit/phpunit": "^9.5.10", + "sebastian/environment": "^5.1.3", + "symfony/console": "^5.4.0 || ^6.0.0", + "symfony/process": "^5.4.0 || ^6.0.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0.0", + "ekino/phpstan-banned-code": "^0.5.0", + "ergebnis/phpstan-rules": "^0.15.3", + "ext-posix": "*", + "infection/infection": "^0.25.3", + "malukenho/mcbumpface": "^1.1.5", + "phpstan/phpstan": "^0.12.99", + "phpstan/phpstan-deprecation-rules": "^0.12.6", + "phpstan/phpstan-phpunit": "^0.12.22", + "phpstan/phpstan-strict-rules": "^0.12.11", + "squizlabs/php_codesniffer": "^3.6.1", + "symfony/filesystem": "^v5.4.0", + "thecodingmachine/phpstan-strict-rules": "^v0.12.2", + "vimeo/psalm": "^4.13.1" + }, + "bin": [ + "bin/paratest" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v6.4.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2021-12-02T09:12:23+00:00" + }, { "name": "dealerdirect/phpcodesniffer-composer-installer", "version": "v0.7.1", @@ -6943,16 +6719,16 @@ }, { "name": "facade/ignition", - "version": "2.16.0", + "version": "2.17.2", "source": { "type": "git", "url": "https://github.com/facade/ignition.git", - "reference": "23400e6cc565c9dcae2c53704b4de1c4870c0697" + "reference": "af3cd70d58ca3ef5189ff0e59efbe5a5c043e2d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facade/ignition/zipball/23400e6cc565c9dcae2c53704b4de1c4870c0697", - "reference": "23400e6cc565c9dcae2c53704b4de1c4870c0697", + "url": "https://api.github.com/repos/facade/ignition/zipball/af3cd70d58ca3ef5189ff0e59efbe5a5c043e2d2", + "reference": "af3cd70d58ca3ef5189ff0e59efbe5a5c043e2d2", "shasum": "" }, "require": { @@ -6969,6 +6745,7 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.14", + "livewire/livewire": "^2.4", "mockery/mockery": "^1.3", "orchestra/testbench": "^5.0|^6.0", "psalm/plugin-laravel": "^1.2" @@ -7016,26 +6793,26 @@ "issues": "https://github.com/facade/ignition/issues", "source": "https://github.com/facade/ignition" }, - "time": "2021-10-28T11:47:23+00:00" + "time": "2021-11-29T14:04:22+00:00" }, { "name": "fakerphp/faker", - "version": "v1.16.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "271d384d216e5e5c468a6b28feedf95d49f83b35" + "reference": "b85e9d44eae8c52cca7aa0939483611f7232b669" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/271d384d216e5e5c468a6b28feedf95d49f83b35", - "reference": "271d384d216e5e5c468a6b28feedf95d49f83b35", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/b85e9d44eae8c52cca7aa0939483611f7232b669", + "reference": "b85e9d44eae8c52cca7aa0939483611f7232b669", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", "psr/container": "^1.0 || ^2.0", - "symfony/deprecation-contracts": "^2.2" + "symfony/deprecation-contracts": "^2.2 || ^3.0" }, "conflict": { "fzaninotto/faker": "*" @@ -7054,7 +6831,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "v1.16-dev" + "dev-main": "v1.17-dev" } }, "autoload": { @@ -7079,9 +6856,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.16.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.17.0" }, - "time": "2021-09-06T14:53:37+00:00" + "time": "2021-12-05T17:14:47+00:00" }, { "name": "filp/whoops", @@ -7695,16 +7472,16 @@ }, { "name": "phpspec/prophecy", - "version": "1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", "shasum": "" }, "require": { @@ -7756,9 +7533,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.14.0" + "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" }, - "time": "2021-09-10T09:02:12+00:00" + "time": "2021-12-08T12:19:24+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -7811,16 +7588,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.8", + "version": "9.2.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e" + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", - "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687", + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687", "shasum": "" }, "require": { @@ -7876,7 +7653,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.8" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10" }, "funding": [ { @@ -7884,20 +7661,20 @@ "type": "github" } ], - "time": "2021-10-30T08:01:38+00:00" + "time": "2021-12-05T09:12:13+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.5", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { @@ -7936,7 +7713,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, "funding": [ { @@ -7944,7 +7721,7 @@ "type": "github" } ], - "time": "2020-09-28T05:57:25+00:00" + "time": "2021-12-02T12:48:52+00:00" }, { "name": "phpunit/php-invoker", @@ -8783,16 +8560,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", - "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", "shasum": "" }, "require": { @@ -8841,14 +8618,14 @@ } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" }, "funding": [ { @@ -8856,7 +8633,7 @@ "type": "github" } ], - "time": "2020-09-28T05:24:23+00:00" + "time": "2021-11-11T14:18:36+00:00" }, { "name": "sebastian/global-state", @@ -9320,16 +9097,16 @@ }, { "name": "slevomat/coding-standard", - "version": "7.0.16", + "version": "7.0.18", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "14c324b2f2f0072933036c2f3abaeda16a56dcd3" + "reference": "b81ac84f41a4797dc25c8ede1b0718e2a74be0fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/14c324b2f2f0072933036c2f3abaeda16a56dcd3", - "reference": "14c324b2f2f0072933036c2f3abaeda16a56dcd3", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b81ac84f41a4797dc25c8ede1b0718e2a74be0fc", + "reference": "b81ac84f41a4797dc25c8ede1b0718e2a74be0fc", "shasum": "" }, "require": { @@ -9341,11 +9118,11 @@ "require-dev": { "phing/phing": "2.17.0", "php-parallel-lint/php-parallel-lint": "1.3.1", - "phpstan/phpstan": "0.12.99", - "phpstan/phpstan-deprecation-rules": "0.12.6", - "phpstan/phpstan-phpunit": "0.12.22", - "phpstan/phpstan-strict-rules": "0.12.11", - "phpunit/phpunit": "7.5.20|8.5.5|9.5.10" + "phpstan/phpstan": "1.2.0", + "phpstan/phpstan-deprecation-rules": "1.0.0", + "phpstan/phpstan-phpunit": "1.0.0", + "phpstan/phpstan-strict-rules": "1.1.0", + "phpunit/phpunit": "7.5.20|8.5.21|9.5.10" }, "type": "phpcodesniffer-standard", "extra": { @@ -9365,7 +9142,7 @@ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/7.0.16" + "source": "https://github.com/slevomat/coding-standard/tree/7.0.18" }, "funding": [ { @@ -9377,20 +9154,20 @@ "type": "tidelift" } ], - "time": "2021-10-22T06:56:51+00:00" + "time": "2021-12-07T17:19:06+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.6.1", + "version": "3.6.2", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e" + "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/f268ca40d54617c6e06757f83f699775c9b3ff2e", - "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a", + "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a", "shasum": "" }, "require": { @@ -9433,7 +9210,7 @@ "source": "https://github.com/squizlabs/PHP_CodeSniffer", "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" }, - "time": "2021-10-11T04:00:11+00:00" + "time": "2021-12-12T21:44:58+00:00" }, { "name": "theseer/tokenizer", @@ -9495,8 +9272,7 @@ "php": "^7", "ext-curl": "*", "ext-dom": "*", - "ext-json": "*", - "ext-memcached": "^3.1" + "ext-json": "*" }, "platform-dev": [], "platform-overrides": { diff --git a/config/api.php b/config/api.php index c8d1914..8f8c4df 100644 --- a/config/api.php +++ b/config/api.php @@ -10,9 +10,11 @@ return [ 'version' => Helper::getPackageInfo()['version'], /** - * Api Token timeout in minutes + * Timestamp timeout in seconds + * Request with timestamp older than 30sec + * will be rejected */ - 'tokenTimeout' => 60, + 'timestampTimeout' => 30, /** * The number of request diff --git a/config/app.php b/config/app.php index 60d5e59..88ba82e 100644 --- a/config/app.php +++ b/config/app.php @@ -3,53 +3,53 @@ return [ /* - |-------------------------------------------------------------------------- - | Application Name - |-------------------------------------------------------------------------- - | - | This value is the name of your application. This value is used when the - | framework needs to place the application's name in a notification or - | any other location as required by the application or its packages. - | + |-------------------------------------------------------------------------- + | Application Name + |-------------------------------------------------------------------------- + | + | This value is the name of your application. This value is used when the + | framework needs to place the application's name in a notification or + | any other location as required by the application or its packages. + | */ 'name' => env('APP_NAME', 'Laravel'), /* - |-------------------------------------------------------------------------- - | Application Environment - |-------------------------------------------------------------------------- - | - | This value determines the "environment" your application is currently - | running in. This may determine how you prefer to configure various - | services the application utilizes. Set this in your ".env" file. - | + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | */ 'env' => env('APP_ENV', 'production'), /* - |-------------------------------------------------------------------------- - | Application Debug Mode - |-------------------------------------------------------------------------- - | - | When your application is in debug mode, detailed error messages with - | stack traces will be shown on every error that occurs within your - | application. If disabled, a simple generic error page is shown. - | + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | */ 'debug' => (bool)env('APP_DEBUG', false), /* - |-------------------------------------------------------------------------- - | Application URL - |-------------------------------------------------------------------------- - | - | This URL is used by the console to properly generate URLs when using - | the Artisan command line tool. You should set this to the root of - | your application so that it is used when running Artisan tasks. - | + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | your application so that it is used when running Artisan tasks. + | */ 'url' => env('APP_URL', 'http://localhost'), @@ -57,66 +57,66 @@ return [ 'asset_url' => env('ASSET_URL', null), /* - |-------------------------------------------------------------------------- - | Application Timezone - |-------------------------------------------------------------------------- - | - | Here you may specify the default timezone for your application, which - | will be used by the PHP date and date-time functions. We have gone - | ahead and set this to a sensible default for you out of the box. - | + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. We have gone + | ahead and set this to a sensible default for you out of the box. + | */ 'timezone' => 'Europe/Paris', /* - |-------------------------------------------------------------------------- - | Application Locale Configuration - |-------------------------------------------------------------------------- - | - | The application locale determines the default locale that will be used - | by the translation service provider. You are free to set this value - | to any of the locales which will be supported by the application. - | + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by the translation service provider. You are free to set this value + | to any of the locales which will be supported by the application. + | */ 'locale' => 'fr', /* - |-------------------------------------------------------------------------- - | Application Fallback Locale - |-------------------------------------------------------------------------- - | - | The fallback locale determines the locale to use when the current one - | is not available. You may change the value to correspond to any of - | the language folders that are provided through your application. - | + |-------------------------------------------------------------------------- + | Application Fallback Locale + |-------------------------------------------------------------------------- + | + | The fallback locale determines the locale to use when the current one + | is not available. You may change the value to correspond to any of + | the language folders that are provided through your application. + | */ 'fallback_locale' => 'en', /* - |-------------------------------------------------------------------------- - | Faker Locale - |-------------------------------------------------------------------------- - | - | This locale will be used by the Faker PHP library when generating fake - | data for your database seeds. For example, this will be used to get - | localized telephone numbers, street address information and more. - | + |-------------------------------------------------------------------------- + | Faker Locale + |-------------------------------------------------------------------------- + | + | This locale will be used by the Faker PHP library when generating fake + | data for your database seeds. For example, this will be used to get + | localized telephone numbers, street address information and more. + | */ 'faker_locale' => 'fr_FR', /* - |-------------------------------------------------------------------------- - | Encryption Key - |-------------------------------------------------------------------------- - | - | This key is used by the Illuminate encrypter service and should be set - | to a random, 32 character string, otherwise these encrypted strings - | will not be safe. Please do this before deploying an application! - | + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is used by the Illuminate encrypter service and should be set + | to a random, 32 character string, otherwise these encrypted strings + | will not be safe. Please do this before deploying an application! + | */ 'key' => env('APP_KEY'), @@ -124,14 +124,14 @@ return [ 'cipher' => 'AES-256-CBC', /* - |-------------------------------------------------------------------------- - | Autoloaded Service Providers - |-------------------------------------------------------------------------- - | - | The service providers listed here will be automatically loaded on the - | request to your application. Feel free to add your own services to - | this array to grant expanded functionality to your applications. - | + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | */ 'providers' => [ @@ -139,6 +139,7 @@ return [ /* * Laravel Framework Service Providers... */ + Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, Illuminate\Bus\BusServiceProvider::class, @@ -169,6 +170,7 @@ return [ /* * Application Service Providers... */ + App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, App\Providers\EventServiceProvider::class, @@ -177,14 +179,14 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Class Aliases - |-------------------------------------------------------------------------- - | - | This array of class aliases will be registered when this application - | is started. However, feel free to register as many as you wish as - | the aliases are "lazy" loaded so they don't hinder performance. - | + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | This array of class aliases will be registered when this application + | is started. However, feel free to register as many as you wish as + | the aliases are "lazy" loaded so they don't hinder performance. + | */ 'aliases' => [ diff --git a/config/auth.php b/config/auth.php index 46b56f6..d634767 100644 --- a/config/auth.php +++ b/config/auth.php @@ -3,14 +3,14 @@ return [ /* - |-------------------------------------------------------------------------- - | Authentication Defaults - |-------------------------------------------------------------------------- - | - | This option controls the default authentication "guard" and password - | reset options for your application. You may change these defaults - | as required, but they're a perfect start for most applications. - | + |-------------------------------------------------------------------------- + | Authentication Defaults + |-------------------------------------------------------------------------- + | + | This option controls the default authentication "guard" and password + | reset options for your application. You may change these defaults + | as required, but they're a perfect start for most applications. + | */ 'defaults' => [ @@ -19,20 +19,20 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Authentication Guards - |-------------------------------------------------------------------------- - | - | Next, you may define every authentication guard for your application. - | Of course, a great default configuration has been defined for you - | here which uses session storage and the Eloquent user provider. - | - | All authentication drivers have a user provider. This defines how the - | users are actually retrieved out of your database or other storage - | mechanisms used by this application to persist your user's data. - | - | Supported: "session", "token" - | + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | here which uses session storage and the Eloquent user provider. + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | Supported: "session", "token" + | */ 'guards' => [ @@ -42,25 +42,25 @@ return [ ], 'api' => [ - 'driver' => 'api-token' + 'driver' => 'api-hmac' ], ], /* - |-------------------------------------------------------------------------- - | User Providers - |-------------------------------------------------------------------------- - | - | All authentication drivers have a user provider. This defines how the - | users are actually retrieved out of your database or other storage - | mechanisms used by this application to persist your user's data. - | - | If you have multiple user tables or models you may configure multiple - | sources which represent each model / table. These sources may then - | be assigned to any extra authentication guards you have defined. - | - | Supported: "database", "eloquent" - | + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | If you have multiple user tables or models you may configure multiple + | sources which represent each model / table. These sources may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | */ 'providers' => [ @@ -71,18 +71,18 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Resetting Passwords - |-------------------------------------------------------------------------- - | - | You may specify multiple password reset configurations if you have more - | than one user table or model in the application and you want to have - | separate password reset settings based on the specific user types. - | - | The expire time is the number of minutes that the reset token should be - | considered valid. This security feature keeps tokens short-lived so - | they have less time to be guessed. You may change this as needed. - | + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | You may specify multiple password reset configurations if you have more + | than one user table or model in the application and you want to have + | separate password reset settings based on the specific user types. + | + | The expire time is the number of minutes that the reset token should be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | */ 'passwords' => [ @@ -95,14 +95,14 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Password Confirmation Timeout - |-------------------------------------------------------------------------- - | - | Here you may define the amount of seconds before a password confirmation - | times out and the user is prompted to re-enter their password via the - | confirmation screen. By default, the timeout lasts for three hours. - | + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the amount of seconds before a password confirmation + | times out and the user is prompted to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | */ 'password_timeout' => 10800, diff --git a/config/broadcasting.php b/config/broadcasting.php index 3bba110..758e391 100644 --- a/config/broadcasting.php +++ b/config/broadcasting.php @@ -3,29 +3,29 @@ return [ /* - |-------------------------------------------------------------------------- - | Default Broadcaster - |-------------------------------------------------------------------------- - | - | This option controls the default broadcaster that will be used by the - | framework when an event needs to be broadcast. You may set this to - | any of the connections defined in the "connections" array below. - | - | Supported: "pusher", "redis", "log", "null" - | + |-------------------------------------------------------------------------- + | Default Broadcaster + |-------------------------------------------------------------------------- + | + | This option controls the default broadcaster that will be used by the + | framework when an event needs to be broadcast. You may set this to + | any of the connections defined in the "connections" array below. + | + | Supported: "pusher", "redis", "log", "null" + | */ 'default' => env('BROADCAST_DRIVER', 'null'), /* - |-------------------------------------------------------------------------- - | Broadcast Connections - |-------------------------------------------------------------------------- - | - | Here you may define all of the broadcast connections that will be used - | to broadcast events to other systems or over websockets. Samples of - | each available type of connection are provided inside this array. - | + |-------------------------------------------------------------------------- + | Broadcast Connections + |-------------------------------------------------------------------------- + | + | Here you may define all of the broadcast connections that will be used + | to broadcast events to other systems or over websockets. Samples of + | each available type of connection are provided inside this array. + | */ 'connections' => [ diff --git a/config/cache.php b/config/cache.php index 9b4d7ab..5740730 100644 --- a/config/cache.php +++ b/config/cache.php @@ -5,30 +5,30 @@ use Illuminate\Support\Str; return [ /* - |-------------------------------------------------------------------------- - | Default Cache Store - |-------------------------------------------------------------------------- - | - | This option controls the default cache connection that gets used while - | using this caching library. This connection is used when another is - | not explicitly specified when executing a given caching function. - | - | Supported: "apc", "array", "database", "file", - | "memcached", "redis", "dynamodb" - | + |-------------------------------------------------------------------------- + | Default Cache Store + |-------------------------------------------------------------------------- + | + | This option controls the default cache connection that gets used while + | using this caching library. This connection is used when another is + | not explicitly specified when executing a given caching function. + | + | Supported: "apc", "array", "database", "file", + | "memcached", "redis", "dynamodb" + | */ 'default' => env('CACHE_DRIVER', 'file'), /* - |-------------------------------------------------------------------------- - | Cache Stores - |-------------------------------------------------------------------------- - | - | Here you may define all of the cache "stores" for your application as - | well as their drivers. You may even define multiple stores for the - | same cache driver to group types of items stored in your caches. - | + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | */ 'stores' => [ @@ -87,14 +87,14 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Cache Key Prefix - |-------------------------------------------------------------------------- - | - | When utilizing a RAM based store such as APC or Memcached, there might - | be other applications utilizing the same cache. So, we'll specify a - | value to get prefixed to all our keys so we can avoid collisions. - | + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing a RAM based store such as APC or Memcached, there might + | be other applications utilizing the same cache. So, we'll specify a + | value to get prefixed to all our keys so we can avoid collisions. + | */ 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache'), diff --git a/config/cors.php b/config/cors.php index 558369d..caccf68 100644 --- a/config/cors.php +++ b/config/cors.php @@ -3,16 +3,16 @@ return [ /* - |-------------------------------------------------------------------------- - | Cross-Origin Resource Sharing (CORS) Configuration - |-------------------------------------------------------------------------- - | - | Here you may configure your settings for cross-origin resource sharing - | or "CORS". This determines what cross-origin operations may execute - | in web browsers. You are free to adjust these settings as needed. - | - | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS - | + |-------------------------------------------------------------------------- + | Cross-Origin Resource Sharing (CORS) Configuration + |-------------------------------------------------------------------------- + | + | Here you may configure your settings for cross-origin resource sharing + | or "CORS". This determines what cross-origin operations may execute + | in web browsers. You are free to adjust these settings as needed. + | + | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS + | */ 'paths' => ['api/*'], diff --git a/config/database.php b/config/database.php index e811670..1130742 100644 --- a/config/database.php +++ b/config/database.php @@ -5,32 +5,32 @@ use Illuminate\Support\Str; return [ /* - |-------------------------------------------------------------------------- - | Default Database Connection Name - |-------------------------------------------------------------------------- - | - | Here you may specify which of the database connections below you wish - | to use as your default connection for all database work. Of course - | you may use many connections at once using the Database library. - | + |-------------------------------------------------------------------------- + | Default Database Connection Name + |-------------------------------------------------------------------------- + | + | Here you may specify which of the database connections below you wish + | to use as your default connection for all database work. Of course + | you may use many connections at once using the Database library. + | */ 'default' => env('DB_CONNECTION', 'mysql'), /* - |-------------------------------------------------------------------------- - | Database Connections - |-------------------------------------------------------------------------- - | - | Here are each of the database connections setup for your application. - | Of course, examples of configuring each database platform that is - | supported by Laravel is shown below to make development simple. - | - | - | All database work in Laravel is done through the PHP PDO facilities - | so make sure you have the driver for your particular database of - | choice installed on your machine before you begin development. - | + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here are each of the database connections setup for your application. + | Of course, examples of configuring each database platform that is + | supported by Laravel is shown below to make development simple. + | + | + | All database work in Laravel is done through the PHP PDO facilities + | so make sure you have the driver for your particular database of + | choice installed on your machine before you begin development. + | */ 'oldDB' => 'oldcrm', @@ -117,27 +117,27 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Migration Repository Table - |-------------------------------------------------------------------------- - | - | This table keeps track of all the migrations that have already run for - | your application. Using this information, we can determine which of - | the migrations on disk haven't actually been run in the database. - | + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run in the database. + | */ 'migrations' => 'migrations', /* - |-------------------------------------------------------------------------- - | Redis Databases - |-------------------------------------------------------------------------- - | - | Redis is an open source, fast, and advanced key-value store that also - | provides a richer body of commands than a typical key-value system - | such as APC or Memcached. Laravel makes it easy to dig right in. - | + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer body of commands than a typical key-value system + | such as APC or Memcached. Laravel makes it easy to dig right in. + | */ 'redis' => [ diff --git a/config/filesystems.php b/config/filesystems.php index f7e7243..659bd8b 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -3,42 +3,42 @@ return [ /* - |-------------------------------------------------------------------------- - | Default Filesystem Disk - |-------------------------------------------------------------------------- - | - | Here you may specify the default filesystem disk that should be used - | by the framework. The "local" disk, as well as a variety of cloud - | based disks are available to your application. Just store away! - | + |-------------------------------------------------------------------------- + | Default Filesystem Disk + |-------------------------------------------------------------------------- + | + | Here you may specify the default filesystem disk that should be used + | by the framework. The "local" disk, as well as a variety of cloud + | based disks are available to your application. Just store away! + | */ 'default' => env('FILESYSTEM_DRIVER', 'local'), /* - |-------------------------------------------------------------------------- - | Default Cloud Filesystem Disk - |-------------------------------------------------------------------------- - | - | Many applications store files both locally and in the cloud. For this - | reason, you may specify a default "cloud" driver here. This driver - | will be bound as the Cloud disk implementation in the container. - | + |-------------------------------------------------------------------------- + | Default Cloud Filesystem Disk + |-------------------------------------------------------------------------- + | + | Many applications store files both locally and in the cloud. For this + | reason, you may specify a default "cloud" driver here. This driver + | will be bound as the Cloud disk implementation in the container. + | */ 'cloud' => env('FILESYSTEM_CLOUD', 's3'), /* - |-------------------------------------------------------------------------- - | Filesystem Disks - |-------------------------------------------------------------------------- - | - | Here you may configure as many filesystem "disks" as you wish, and you - | may even configure multiple disks of the same driver. Defaults have - | been setup for each driver as an example of the required options. - | - | Supported Drivers: "local", "ftp", "sftp", "s3" - | + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Here you may configure as many filesystem "disks" as you wish, and you + | may even configure multiple disks of the same driver. Defaults have + | been setup for each driver as an example of the required options. + | + | Supported Drivers: "local", "ftp", "sftp", "s3" + | */ 'disks' => [ @@ -73,14 +73,14 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Symbolic Links - |-------------------------------------------------------------------------- - | - | Here you may configure the symbolic links that will be created when the - | `storage:link` Artisan command is executed. The array keys should be - | the locations of the links and the values should be their targets. - | + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | */ 'links' => [ diff --git a/config/hashing.php b/config/hashing.php index 108db6a..797fcfe 100644 --- a/config/hashing.php +++ b/config/hashing.php @@ -3,29 +3,29 @@ return [ /* - |-------------------------------------------------------------------------- - | Default Hash Driver - |-------------------------------------------------------------------------- - | - | This option controls the default hash driver that will be used to hash - | passwords for your application. By default, the bcrypt algorithm is - | used; however, you remain free to modify this option if you wish. - | - | Supported: "bcrypt", "argon", "argon2id" - | + |-------------------------------------------------------------------------- + | Default Hash Driver + |-------------------------------------------------------------------------- + | + | This option controls the default hash driver that will be used to hash + | passwords for your application. By default, the bcrypt algorithm is + | used; however, you remain free to modify this option if you wish. + | + | Supported: "bcrypt", "argon", "argon2id" + | */ 'driver' => 'bcrypt', /* - |-------------------------------------------------------------------------- - | Bcrypt Options - |-------------------------------------------------------------------------- - | - | Here you may specify the configuration options that should be used when - | passwords are hashed using the Bcrypt algorithm. This will allow you - | to control the amount of time it takes to hash the given password. - | + |-------------------------------------------------------------------------- + | Bcrypt Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Bcrypt algorithm. This will allow you + | to control the amount of time it takes to hash the given password. + | */ 'bcrypt' => [ @@ -34,14 +34,14 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Argon Options - |-------------------------------------------------------------------------- - | - | Here you may specify the configuration options that should be used when - | passwords are hashed using the Argon algorithm. These will allow you - | to control the amount of time it takes to hash the given password. - | + |-------------------------------------------------------------------------- + | Argon Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Argon algorithm. These will allow you + | to control the amount of time it takes to hash the given password. + | */ 'argon' => [ diff --git a/config/logging.php b/config/logging.php index 6aa77fe..1780b16 100644 --- a/config/logging.php +++ b/config/logging.php @@ -7,31 +7,31 @@ use Monolog\Handler\SyslogUdpHandler; return [ /* - |-------------------------------------------------------------------------- - | Default Log Channel - |-------------------------------------------------------------------------- - | - | This option defines the default log channel that gets used when writing - | messages to the logs. The name specified in this option should match - | one of the channels defined in the "channels" configuration array. - | + |-------------------------------------------------------------------------- + | Default Log Channel + |-------------------------------------------------------------------------- + | + | This option defines the default log channel that gets used when writing + | messages to the logs. The name specified in this option should match + | one of the channels defined in the "channels" configuration array. + | */ 'default' => env('LOG_CHANNEL', 'stack'), /* - |-------------------------------------------------------------------------- - | Log Channels - |-------------------------------------------------------------------------- - | - | Here you may configure the log channels for your application. Out of - | the box, Laravel uses the Monolog PHP logging library. This gives - | you a variety of powerful log handlers / formatters to utilize. - | - | Available Drivers: "single", "daily", "slack", "syslog", - | "errorlog", "monolog", - | "custom", "stack" - | + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Out of + | the box, Laravel uses the Monolog PHP logging library. This gives + | you a variety of powerful log handlers / formatters to utilize. + | + | Available Drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", + | "custom", "stack" + | */ 'channels' => [ diff --git a/config/mail.php b/config/mail.php index 8481227..3818d05 100644 --- a/config/mail.php +++ b/config/mail.php @@ -3,34 +3,34 @@ return [ /* - |-------------------------------------------------------------------------- - | Default Mailer - |-------------------------------------------------------------------------- - | - | This option controls the default mailer that is used to send any email - | messages sent by your application. Alternative mailers may be setup - | and used as needed; however, this mailer will be used by default. - | + |-------------------------------------------------------------------------- + | Default Mailer + |-------------------------------------------------------------------------- + | + | This option controls the default mailer that is used to send any email + | messages sent by your application. Alternative mailers may be setup + | and used as needed; however, this mailer will be used by default. + | */ 'default' => env('MAIL_MAILER', 'smtp'), /* - |-------------------------------------------------------------------------- - | Mailer Configurations - |-------------------------------------------------------------------------- - | - | Here you may configure all of the mailers used by your application plus - | their respective settings. Several examples have been configured for - | you and you are free to add your own as your application requires. - | - | Laravel supports a variety of mail "transport" drivers to be used while - | sending an e-mail. You will specify which one you are using for your - | mailers below. You are free to add additional mailers as required. - | - | Supported: "smtp", "sendmail", "mailgun", "ses", - | "postmark", "log", "array" - | + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers to be used while + | sending an e-mail. You will specify which one you are using for your + | mailers below. You are free to add additional mailers as required. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", + | "postmark", "log", "array" + | */ 'mailers' => [ @@ -73,14 +73,14 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Global "From" Address - |-------------------------------------------------------------------------- - | - | You may wish for all e-mails sent by your application to be sent from - | the same address. Here, you may specify a name and address that is - | used globally for all e-mails that are sent by your application. - | + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | */ 'from' => [ @@ -95,14 +95,14 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Markdown Mail Settings - |-------------------------------------------------------------------------- - | - | If you are using Markdown based email rendering, you may configure your - | theme and component paths here, allowing you to customize the design - | of the emails. Or, you may simply stick with the Laravel defaults! - | + |-------------------------------------------------------------------------- + | Markdown Mail Settings + |-------------------------------------------------------------------------- + | + | If you are using Markdown based email rendering, you may configure your + | theme and component paths here, allowing you to customize the design + | of the emails. Or, you may simply stick with the Laravel defaults! + | */ 'markdown' => [ diff --git a/config/queue.php b/config/queue.php index 1222296..08e1704 100644 --- a/config/queue.php +++ b/config/queue.php @@ -3,29 +3,29 @@ return [ /* - |-------------------------------------------------------------------------- - | Default Queue Connection Name - |-------------------------------------------------------------------------- - | - | Laravel's queue API supports an assortment of back-ends via a single - | API, giving you convenient access to each back-end using the same - | syntax for every one. Here you may define a default connection. - | + |-------------------------------------------------------------------------- + | Default Queue Connection Name + |-------------------------------------------------------------------------- + | + | Laravel's queue API supports an assortment of back-ends via a single + | API, giving you convenient access to each back-end using the same + | syntax for every one. Here you may define a default connection. + | */ 'default' => env('QUEUE_CONNECTION', 'sync'), /* - |-------------------------------------------------------------------------- - | Queue Connections - |-------------------------------------------------------------------------- - | - | Here you may configure the connection information for each server that - | is used by your application. A default configuration has been added - | for each back-end shipped with Laravel. You are free to add more. - | - | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" - | + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Laravel. You are free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | */ 'connections' => [ @@ -70,14 +70,14 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Failed Queue Jobs - |-------------------------------------------------------------------------- - | - | These options configure the behavior of failed queue job logging so you - | can control which database and table are used to store the jobs that - | have failed. You may change them to any database / table you wish. - | + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control which database and table are used to store the jobs that + | have failed. You may change them to any database / table you wish. + | */ 'failed' => [ diff --git a/config/services.php b/config/services.php index 2a1d616..915dfe7 100644 --- a/config/services.php +++ b/config/services.php @@ -3,15 +3,15 @@ return [ /* - |-------------------------------------------------------------------------- - | Third Party Services - |-------------------------------------------------------------------------- - | - | This file is for storing the credentials for third party services such - | as Mailgun, Postmark, AWS and more. This file provides the de facto - | location for this type of information, allowing packages to have - | a conventional file to locate the various service credentials. - | + |-------------------------------------------------------------------------- + | Third Party Services + |-------------------------------------------------------------------------- + | + | This file is for storing the credentials for third party services such + | as Mailgun, Postmark, AWS and more. This file provides the de facto + | location for this type of information, allowing packages to have + | a conventional file to locate the various service credentials. + | */ 'mailgun' => [ diff --git a/config/session.php b/config/session.php index c0e798b..13ef89b 100644 --- a/config/session.php +++ b/config/session.php @@ -5,30 +5,30 @@ use Illuminate\Support\Str; return [ /* - |-------------------------------------------------------------------------- - | Default Session Driver - |-------------------------------------------------------------------------- - | - | This option controls the default session "driver" that will be used on - | requests. By default, we will use the lightweight native driver but - | you may specify any of the other wonderful drivers provided here. - | - | Supported: "file", "cookie", "database", "apc", - | "memcached", "redis", "dynamodb", "array" - | + |-------------------------------------------------------------------------- + | Default Session Driver + |-------------------------------------------------------------------------- + | + | This option controls the default session "driver" that will be used on + | requests. By default, we will use the lightweight native driver but + | you may specify any of the other wonderful drivers provided here. + | + | Supported: "file", "cookie", "database", "apc", + | "memcached", "redis", "dynamodb", "array" + | */ 'driver' => env('SESSION_DRIVER', 'file'), /* - |-------------------------------------------------------------------------- - | Session Lifetime - |-------------------------------------------------------------------------- - | - | Here you may specify the number of minutes that you wish the session - | to be allowed to remain idle before it expires. If you want them - | to immediately expire on the browser closing, set that option. - | + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to immediately expire on the browser closing, set that option. + | */ 'lifetime' => env('SESSION_LIFETIME', 120), @@ -36,94 +36,94 @@ return [ 'expire_on_close' => false, /* - |-------------------------------------------------------------------------- - | Session Encryption - |-------------------------------------------------------------------------- - | - | This option allows you to easily specify that all of your session data - | should be encrypted before it is stored. All encryption will be run - | automatically by Laravel and you can use the Session like normal. - | + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it is stored. All encryption will be run + | automatically by Laravel and you can use the Session like normal. + | */ 'encrypt' => false, /* - |-------------------------------------------------------------------------- - | Session File Location - |-------------------------------------------------------------------------- - | - | When using the native session driver, we need a location where session - | files may be stored. A default has been set for you but a different - | location may be specified. This is only needed for file sessions. - | + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When using the native session driver, we need a location where session + | files may be stored. A default has been set for you but a different + | location may be specified. This is only needed for file sessions. + | */ 'files' => storage_path('framework/sessions'), /* - |-------------------------------------------------------------------------- - | Session Database Connection - |-------------------------------------------------------------------------- - | - | When using the "database" or "redis" session drivers, you may specify a - | connection that should be used to manage these sessions. This should - | correspond to a connection in your database configuration options. - | + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | */ 'connection' => env('SESSION_CONNECTION', null), /* - |-------------------------------------------------------------------------- - | Session Database Table - |-------------------------------------------------------------------------- - | - | When using the "database" session driver, you may specify the table we - | should use to manage the sessions. Of course, a sensible default is - | provided for you; however, you are free to change this as needed. - | + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table we + | should use to manage the sessions. Of course, a sensible default is + | provided for you; however, you are free to change this as needed. + | */ 'table' => 'sessions', /* - |-------------------------------------------------------------------------- - | Session Cache Store - |-------------------------------------------------------------------------- - | - | While using one of the framework's cache driven session backends you may - | list a cache store that should be used for these sessions. This value - | must match with one of the application's configured cache "stores". - | - | Affects: "apc", "dynamodb", "memcached", "redis" - | + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | While using one of the framework's cache driven session backends you may + | list a cache store that should be used for these sessions. This value + | must match with one of the application's configured cache "stores". + | + | Affects: "apc", "dynamodb", "memcached", "redis" + | */ 'store' => env('SESSION_STORE', null), /* - |-------------------------------------------------------------------------- - | Session Sweeping Lottery - |-------------------------------------------------------------------------- - | - | Some session drivers must manually sweep their storage location to get - | rid of old sessions from storage. Here are the chances that it will - | happen on a given request. By default, the odds are 2 out of 100. - | + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | */ 'lottery' => [2, 100], /* - |-------------------------------------------------------------------------- - | Session Cookie Name - |-------------------------------------------------------------------------- - | - | Here you may change the name of the cookie used to identify a session - | instance by ID. The name specified here will get used every time a - | new session cookie is created by the framework for every driver. - | + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the cookie used to identify a session + | instance by ID. The name specified here will get used every time a + | new session cookie is created by the framework for every driver. + | */ 'cookie' => env( @@ -132,68 +132,68 @@ return [ ), /* - |-------------------------------------------------------------------------- - | Session Cookie Path - |-------------------------------------------------------------------------- - | - | The session cookie path determines the path for which the cookie will - | be regarded as available. Typically, this will be the root path of - | your application but you are free to change this when necessary. - | + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application but you are free to change this when necessary. + | */ 'path' => '/', /* - |-------------------------------------------------------------------------- - | Session Cookie Domain - |-------------------------------------------------------------------------- - | - | Here you may change the domain of the cookie used to identify a session - | in your application. This will determine which domains the cookie is - | available to in your application. A sensible default has been set. - | + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | Here you may change the domain of the cookie used to identify a session + | in your application. This will determine which domains the cookie is + | available to in your application. A sensible default has been set. + | */ 'domain' => env('SESSION_DOMAIN', null), /* - |-------------------------------------------------------------------------- - | HTTPS Only Cookies - |-------------------------------------------------------------------------- - | - | By setting this option to true, session cookies will only be sent back - | to the server if the browser has a HTTPS connection. This will keep - | the cookie from being sent to you if it can not be done securely. - | + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you if it can not be done securely. + | */ 'secure' => env('SESSION_SECURE_COOKIE'), /* - |-------------------------------------------------------------------------- - | HTTP Access Only - |-------------------------------------------------------------------------- - | - | Setting this value to true will prevent JavaScript from accessing the - | value of the cookie and the cookie will only be accessible through - | the HTTP protocol. You are free to modify this option if needed. - | + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. You are free to modify this option if needed. + | */ 'http_only' => true, /* - |-------------------------------------------------------------------------- - | Same-Site Cookies - |-------------------------------------------------------------------------- - | - | This option determines how your cookies behave when cross-site requests - | take place, and can be used to mitigate CSRF attacks. By default, we - | will set this value to "lax" since this is a secure default value. - | - | Supported: "lax", "strict", "none", null - | + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | will set this value to "lax" since this is a secure default value. + | + | Supported: "lax", "strict", "none", null + | */ 'same_site' => 'lax', diff --git a/config/trustedproxy.php b/config/trustedproxy.php index 376df3b..e18fa5d 100644 --- a/config/trustedproxy.php +++ b/config/trustedproxy.php @@ -15,10 +15,14 @@ return [ * of your proxy (e.g. if using ELB or similar). * */ + 'proxies' => [ - '192.168.31.9', // HA PROXY prod "Virtual IP" - '192.168.31.10', // HA PROXY prod "Virtual IP" redondance - '192.168.34.15' // HA PROXY dev "Virtual IP" + // * HA PROXY prod "Virtual IP". + '192.168.31.9', + // * HA PROXY prod "Virtual IP" redondance. + '192.168.31.10', + // * HA PROXY dev "Virtual IP". + '192.168.34.15' ], /* @@ -46,6 +50,7 @@ return [ * * @link https://symfony.com/doc/current/deployment/proxies.html */ + 'headers' => Illuminate\Http\Request::HEADER_X_FORWARDED_FOR | Illuminate\Http\Request::HEADER_X_FORWARDED_HOST | Illuminate\Http\Request::HEADER_X_FORWARDED_PORT | diff --git a/config/view.php b/config/view.php index 22b8a18..53008fd 100644 --- a/config/view.php +++ b/config/view.php @@ -3,14 +3,14 @@ return [ /* - |-------------------------------------------------------------------------- - | View Storage Paths - |-------------------------------------------------------------------------- - | - | Most templating systems load templates from disk. Here you may specify - | an array of paths that should be checked for your views. Of course - | the usual Laravel view path has already been registered for you. - | + |-------------------------------------------------------------------------- + | View Storage Paths + |-------------------------------------------------------------------------- + | + | Most templating systems load templates from disk. Here you may specify + | an array of paths that should be checked for your views. Of course + | the usual Laravel view path has already been registered for you. + | */ 'paths' => [ @@ -18,14 +18,14 @@ return [ ], /* - |-------------------------------------------------------------------------- - | Compiled View Path - |-------------------------------------------------------------------------- - | - | This option determines where all the compiled Blade templates will be - | stored for your application. Typically, this is within the storage - | directory. However, as usual, you are free to change this value. - | + |-------------------------------------------------------------------------- + | Compiled View Path + |-------------------------------------------------------------------------- + | + | This option determines where all the compiled Blade templates will be + | stored for your application. Typically, this is within the storage + | directory. However, as usual, you are free to change this value. + | */ 'compiled' => env( diff --git a/database/factories/AnswerFactory.php b/database/factories/AnswerFactory.php index b9dcbd0..2d810c0 100644 --- a/database/factories/AnswerFactory.php +++ b/database/factories/AnswerFactory.php @@ -7,7 +7,6 @@ use Illuminate\Database\Eloquent\Factories\Factory; class AnswerFactory extends Factory { - /** * The name of the factory's corresponding model. * diff --git a/database/factories/BrandFactory.php b/database/factories/BrandFactory.php index d8b08aa..777ac2b 100644 --- a/database/factories/BrandFactory.php +++ b/database/factories/BrandFactory.php @@ -7,7 +7,6 @@ use Illuminate\Database\Eloquent\Factories\Factory; class BrandFactory extends Factory { - /** * The name of the factory's corresponding model. * diff --git a/database/factories/CustomerFactory.php b/database/factories/CustomerFactory.php index 851e862..be9fad3 100644 --- a/database/factories/CustomerFactory.php +++ b/database/factories/CustomerFactory.php @@ -7,7 +7,6 @@ use Illuminate\Database\Eloquent\Factories\Factory; class CustomerFactory extends Factory { - /** * The name of the factory's corresponding model. * diff --git a/database/factories/CustomerInfoFactory.php b/database/factories/CustomerInfoFactory.php index e1b7525..2a33709 100644 --- a/database/factories/CustomerInfoFactory.php +++ b/database/factories/CustomerInfoFactory.php @@ -5,10 +5,11 @@ namespace Database\Factories; use App\Enums\Country; use App\Models\CustomerInfo; use Illuminate\Database\Eloquent\Factories\Factory; +use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\ValidationException; class CustomerInfoFactory extends Factory { - /** * The name of the factory's corresponding model. * @@ -23,23 +24,29 @@ class CustomerInfoFactory extends Factory */ public function definition() { - $this->faker->addProvider(new \Faker\Provider\fr_FR\Address($this->faker)); - $this->faker->addProvider(new \Faker\Provider\fr_FR\PhoneNumber($this->faker)); + $faker = \Faker\Factory::create('fr_FR'); + $faker->addProvider(new \Faker\Provider\fr_FR\Address($faker)); + $faker->addProvider(new \Faker\Provider\fr_FR\PhoneNumber($faker)); $countries = Country::toArray(); $country = $countries[\array_rand($countries)]; + $phone = $faker->phoneNumber; + try { + Validator::validate(['phone' => $phone], ['phone' => 'string|phone:FR,BE,LU']); + } catch (ValidationException $e) { + $phone = '0388982532'; + } return [ - 'firstname' => $this->faker->firstName, - 'lastname' => $this->faker->lastName, - 'birth_date' => $this->faker->dateTimeBetween('-60 years', '-18 years'), - 'address' => $this->faker->address, + 'firstname' => $faker->firstName, + 'lastname' => $faker->lastName, + 'birth_date' => $faker->dateTimeBetween('-60 years', '-18 years'), + 'address' => $faker->address, 'address2' => '', - 'phone' => $this->faker->phoneNumber, + 'phone' => $phone, 'country' => $country, - 'newcountry' => $country, - 'city' => $this->faker->city, - 'postcode' => $this->faker->postcode, - 'opt_in' => $this->faker->boolean, - 'hashcode' => $this->faker->sha1 + 'city' => $faker->city, + 'postcode' => $faker->postcode, + 'opt_in' => $faker->boolean, + 'hashcode' => $faker->sha1 ]; } } diff --git a/database/factories/CustomerLoginFactory.php b/database/factories/CustomerLoginFactory.php index 056ff0e..2ef783c 100644 --- a/database/factories/CustomerLoginFactory.php +++ b/database/factories/CustomerLoginFactory.php @@ -7,7 +7,11 @@ use Illuminate\Database\Eloquent\Factories\Factory; class CustomerLoginFactory extends Factory { - + /** + * Keep in memory if factory produced more than one model + * + * @var boolean + */ protected static $first = false; /** @@ -25,7 +29,7 @@ class CustomerLoginFactory extends Factory public function definition() { $bool = $this->faker->boolean; - // Force first to be active + // * Force first to be active. if (!self::$first) { self::$first = true; $bool = false; diff --git a/database/factories/QuestionFactory.php b/database/factories/QuestionFactory.php index 7004794..d86a99d 100644 --- a/database/factories/QuestionFactory.php +++ b/database/factories/QuestionFactory.php @@ -8,7 +8,6 @@ use Illuminate\Database\Eloquent\Factories\Factory; class QuestionFactory extends Factory { - /** * The name of the factory's corresponding model. * diff --git a/database/factories/ServiceFactory.php b/database/factories/ServiceFactory.php index 5d18187..00a3a8f 100644 --- a/database/factories/ServiceFactory.php +++ b/database/factories/ServiceFactory.php @@ -7,7 +7,6 @@ use Illuminate\Database\Eloquent\Factories\Factory; class ServiceFactory extends Factory { - /** * The name of the factory's corresponding model. * diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index af316d5..e16f587 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -8,7 +8,6 @@ use Illuminate\Support\Str; class UserFactory extends Factory { - /** * The name of the factory's corresponding model. * @@ -27,7 +26,8 @@ class UserFactory extends Factory 'name' => $this->faker->name, 'email' => $this->faker->unique()->safeEmail, 'email_verified_at' => now(), - 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + // * password => 'password' + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'remember_token' => Str::random(10), ]; } diff --git a/database/migrations/2021_12_07_203437_drop_api_token_from_services.php b/database/migrations/2021_12_07_203437_drop_api_token_from_services.php new file mode 100644 index 0000000..9068601 --- /dev/null +++ b/database/migrations/2021_12_07_203437_drop_api_token_from_services.php @@ -0,0 +1,34 @@ +dropColumn('api_token'); + $table->dropColumn('api_token_date'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('services', function (Blueprint $table) { + $table->string('api_token')->nullable()->after('bearer_token_date'); + $table->timestamp('api_token_date')->after('api_token')->useCurrent(); + }); + } +} diff --git a/database/migrations/2021_12_07_211411_set_unique_constrain_on_api_token_on_services.php b/database/migrations/2021_12_07_211411_set_unique_constrain_on_api_token_on_services.php new file mode 100644 index 0000000..229aa73 --- /dev/null +++ b/database/migrations/2021_12_07_211411_set_unique_constrain_on_api_token_on_services.php @@ -0,0 +1,32 @@ +unique('bearer_token'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('services', function (Blueprint $table) { + $table->dropUnique(['bearer_token']); + }); + } +} diff --git a/database/migrations/2021_12_08_000551_increase_bearer_pwd_on_services.php b/database/migrations/2021_12_08_000551_increase_bearer_pwd_on_services.php new file mode 100644 index 0000000..c5404ce --- /dev/null +++ b/database/migrations/2021_12_08_000551_increase_bearer_pwd_on_services.php @@ -0,0 +1,31 @@ +string('bearer_pwd', 312)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // Dont revert this, could break things with truncated + // columns, wont harm. + } +} diff --git a/database/migrations/2021_12_26_152449_parse_country_on_customer_infos.php b/database/migrations/2021_12_26_152449_parse_country_on_customer_infos.php new file mode 100644 index 0000000..f9404ff --- /dev/null +++ b/database/migrations/2021_12_26_152449_parse_country_on_customer_infos.php @@ -0,0 +1,65 @@ +count() / $perPage); + dump("items per page $perPage"); + for ($currentPage; $currentPage <= $totalPages; $currentPage++) { + dump("page $currentPage"); + DB::transaction(function () use ($currentPage, $perPage) { + collect(CustomerInfo::query() + ->simplePaginate($perPage, ['*'], 'page', $currentPage)->items()) + ->map(function (CustomerInfo $info) { + try { + $country = Str::camel($info->getRawOriginal('country') ?? ''); + $country = $country === 'belgique' ? 'belgium' : $country; + $country = $country === 'suisse' ? 'switzerland' : $country; + $country = $country === 'Afghanistan' ? 'france' : $country; + if (is_numeric($country)) { + $country = 'france'; + } + $info->newcountry = Country::make($country)->value; + } catch (Exception $e) { + $info->newcountry = Country::france()->value; + } finally { + if ($info->newcountry === Country::afghanistan()->value) { + $info->newcountry = Country::france()->value; + } + $info->saveOrFail(); + } //end try + }); + }); + } //end for + Schema::table('customer_infos', function (Blueprint $table) { + $table->dropColumn('country'); + $table->renameColumn('newcountry', 'country'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // Do nothing. + } +} diff --git a/database/migrations/2021_12_28_010744_set_customer_id_nullable_on_customer_answer.php b/database/migrations/2021_12_28_010744_set_customer_id_nullable_on_customer_answer.php new file mode 100644 index 0000000..b21e272 --- /dev/null +++ b/database/migrations/2021_12_28_010744_set_customer_id_nullable_on_customer_answer.php @@ -0,0 +1,42 @@ +bigInteger('customer_id')->unsigned()->nullable()->change(); + $table->dropForeign(['customer_id']); + $table->foreign('customer_id')->references('id') + ->on('customers')->nullOnDelete(); + }); + Schema::enableForeignKeyConstraints(); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::disableForeignKeyConstraints(); + Schema::table('customer_answer', function (Blueprint $table) { + $table->bigInteger('customer_id')->unsigned()->change(); + $table->dropForeign(['customer_id']); + $table->foreign('customer_id')->references('id') + ->on('customers'); + }); + Schema::enableForeignKeyConstraints(); + } +} diff --git a/database/seeders/BrandSeeder.php b/database/seeders/BrandSeeder.php index f71cc8f..0012ec6 100644 --- a/database/seeders/BrandSeeder.php +++ b/database/seeders/BrandSeeder.php @@ -17,7 +17,7 @@ class BrandSeeder extends Seeder */ public function run() { - /** @var \App\Models\Brand $brand*/ + /** @var \App\Models\Brand $brand */ Brand::factory(10)->create()->each(function ($brand) { Service::factory(1)->make()->each( /** @var \App\Models\Service $service*/ diff --git a/database/seeders/BrandStatSeeder.php b/database/seeders/BrandStatSeeder.php index 03bc5f3..d1d6a52 100644 --- a/database/seeders/BrandStatSeeder.php +++ b/database/seeders/BrandStatSeeder.php @@ -17,7 +17,7 @@ class BrandStatSeeder extends Seeder public function run() { BrandStat::truncate(); - // on génère 1 an pour chaque marque + // * On génère 1 an pour chaque marque. $brands = Brand::all(); /** @var \App\Models\Brand $brand*/ foreach ($brands as $brand) { @@ -27,11 +27,11 @@ class BrandStatSeeder extends Seeder $new = rand(10, 30) / 100; for ($i = 365; $i > 0; $i--) { $total += floor($i / 2); - // pourcentage active + // * Pourcentage active. $pActive = $active + ($i * 100 / 365) / 100 - 0.2; $pActive = $pActive < 0.3 ? 0.3 : $pActive; $pInactive = 1 - $pActive; - // pourcentage nouveau + // * Pourcentage nouveau. $pNew = $new; $pOld = 1 - $pNew; @@ -41,13 +41,14 @@ class BrandStatSeeder extends Seeder 'old_accounts' => \ceil($total * $pOld), 'new_accounts' => \floor($total * $pNew), 'total_accounts' => $total, - 'total_opt_in' => ceil($total * 0.15) // 15% optin + // 15% optin + 'total_opt_in' => ceil($total * 0.15) ]); $brandStat->brand()->associate($brand); $brandStat->created_at = $startDay; - $brandStat->save(); + $brandStat->saveOrFail(); $startDay = $startDay->subDay(); - } - } + }//end for + }//end foreach } } diff --git a/database/seeders/CustomerSeeder.php b/database/seeders/CustomerSeeder.php index a9933a2..ca3316c 100644 --- a/database/seeders/CustomerSeeder.php +++ b/database/seeders/CustomerSeeder.php @@ -17,8 +17,9 @@ class CustomerSeeder extends Seeder */ public function run() { + $hasSetAtLeasOneActivated = false; Customer::factory(10)->create()->each( - function ($customer) { + function ($customer) use (&$hasSetAtLeasOneActivated) { /** @var \App\Models\Brand */ foreach (Brand::all() as $brand) { CustomerInfo::factory(1)->make()->each( @@ -29,9 +30,13 @@ class CustomerSeeder extends Seeder } ); CustomerLogin::factory(1)->make()->each( - function ($customerLogin) use ($customer, $brand) { + function ($customerLogin) use ($customer, $brand, &$hasSetAtLeasOneActivated) { $customerLogin->customer()->associate($customer); $customerLogin->brand()->associate($brand); + if (!$hasSetAtLeasOneActivated) { + $hasSetAtLeasOneActivated = true; + $customerLogin->status = true; + } $customerLogin->save(); } ); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..17d86f2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,17645 @@ +{ + "name": "crm", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "axios": "^0.21", + "clean-webpack-plugin": "^4.0.0", + "laravel-mix": "^6.0.6", + "laravel-mix-clean": "^0.1.0", + "laravel-mix-polyfill": "^3.0.0", + "moment": "^2.29.1", + "postcss": "^8.1.14", + "postcss-discard-comments": "^4.0.2", + "resolve-url-loader": "^3.1.3", + "sass": "^1.32.11", + "sass-loader": "^11.0.1", + "swiper": "^6.6.1", + "vue": "^2.6.12", + "vue-loader": "^15.9.6", + "vue-template-compiler": "^2.6.12" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.0.tgz", + "integrity": "sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", + "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helpers": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", + "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz", + "integrity": "sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.0.tgz", + "integrity": "sha512-9KuleLT0e77wFUku6TUkqZzCEymBdtuQQ27MhEKzf9UOOJu3cYj98kyaDAzxpC7lV6DGiZFuC8XqDsq8/Kl6aQ==", + "dev": true, + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.0.tgz", + "integrity": "sha512-S7iaOT1SYlqK0sQaCi21RX4+13hmdmnxIEAnQUB/eh7GeAnRjOUgTYpLkUOiRXzD+yog1JxP0qyAQZ7ZxVxLVg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.0.tgz", + "integrity": "sha512-XLwWvqEaq19zFlF5PTgOod4bUA+XbkR4WLQBct1bkzmxJGB0ZEJaoKF4c8cgH9oBtCDuYJ8BP5NB9uFiEgO5QA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-member-expression-to-functions": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.0.tgz", + "integrity": "sha512-3DyG0zAFAZKcOp7aVr33ddwkxJ0Z0Jr5V99y3I690eYLpukJsJvAbzTy1ewoCqsML8SbIrjH14Jc/nSQ4TvNPA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "regexpu-core": "^4.7.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.4.tgz", + "integrity": "sha512-OrpPZ97s+aPi6h2n1OXzdhVis1SGSsMU2aMHgLcOKfsp4/v1NWpx3CWT3lBj5eeBq9cDkPkh+YCfdF7O12uNDQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.0.tgz", + "integrity": "sha512-Hk2SLxC9ZbcOhLpg/yMznzJ11W++lg5GMbxt1ev6TXUiJB0N42KPC+7w8a+eWGuqDnUYuwStJoZHM7RgmIOaGQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", + "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", + "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", + "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", + "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", + "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-simple-access": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", + "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.0.tgz", + "integrity": "sha512-MLM1IOMe9aQBqMWxcRw8dcb9jlM86NIw7KA0Wri91Xkfied+dE0QuBFSBjMNvqzmS0OSIDsMNC24dBEkPUi7ew==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-wrap-function": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", + "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", + "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", + "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.0.tgz", + "integrity": "sha512-VVMGzYY3vkWgCJML+qVLvGIam902mJW0FvT7Avj1zEe0Gn7D93aWdLblYARTxEw+6DhZmtzhBM2zv0ekE5zg1g==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.0.tgz", + "integrity": "sha512-dVRM0StFMdKlkt7cVcGgwD8UMaBfWJHl3A83Yfs8GQ3MO0LHIIIMvK7Fa0RGOGUQ10qikLaX6D7o5htcQWgTMQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.15.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.16.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.2.tgz", + "integrity": "sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.16.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.2.tgz", + "integrity": "sha512-h37CvpLSf8gb2lIJ2CgC3t+EjFbi0t8qS7LCS1xcJIlEXE4czlofwaW7W1HA8zpgOCzI9C1nmoqNR1zWkk0pQg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.0.tgz", + "integrity": "sha512-4tcFwwicpWTrpl9qjf7UsoosaArgImF85AxqCRZlgc3IQDvkUHjJpruXAL58Wmj+T6fypWTC/BakfEkwIL/pwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.0.tgz", + "integrity": "sha512-nyYmIo7ZqKsY6P4lnVmBlxp9B3a96CscbLotlsNuktMHahkDwoPYEjXrZHU0Tj844Z9f1IthVxQln57mhkcExw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.16.0", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.0.tgz", + "integrity": "sha512-mCF3HcuZSY9Fcx56Lbn+CGdT44ioBMMvjNVldpKtj8tpniETdLjnxdHI1+sDWXIM1nNt+EanJOZ3IG9lzVjs7A==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.0.tgz", + "integrity": "sha512-mAy3sdcY9sKAkf3lQbDiv3olOfiLqI51c9DR9b19uMoR2Z6r5pmGl7dfNFqEvqOyqbf1ta4lknK4gc5PJn3mfA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.0.tgz", + "integrity": "sha512-QGSA6ExWk95jFQgwz5GQ2Dr95cf7eI7TKutIXXTb7B1gCLTCz5hTjFTQGfLFBBiC5WSNi7udNwWsqbbMh1c4yQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.0.tgz", + "integrity": "sha512-CjI4nxM/D+5wCnhD11MHB1AwRSAYeDT+h8gCdcVJZ/OK7+wRzFsf7PFPWVpVpNRkHMmMkQWAHpTq+15IXQ1diA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.0.tgz", + "integrity": "sha512-kouIPuiv8mSi5JkEhzApg5Gn6hFyKPnlkO0a9YSzqRurH8wYzSlf6RJdzluAsbqecdW5pBvDJDfyDIUR/vLxvg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.0.tgz", + "integrity": "sha512-pbW0fE30sVTYXXm9lpVQQ/Vc+iTeQKiXlaNRZPPN2A2VdlWyAtsUrsQ3xydSlDW00TFMK7a8m3cDTkBF5WnV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.0.tgz", + "integrity": "sha512-3bnHA8CAFm7cG93v8loghDYyQ8r97Qydf63BeYiGgYbjKKB/XP53W15wfRC7dvKfoiJ34f6Rbyyx2btExc8XsQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.0.tgz", + "integrity": "sha512-FAhE2I6mjispy+vwwd6xWPyEx3NYFS13pikDBWUAFGZvq6POGs5eNchw8+1CYoEgBl9n11I3NkzD7ghn25PQ9Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.0.tgz", + "integrity": "sha512-LU/+jp89efe5HuWJLmMmFG0+xbz+I2rSI7iLc1AlaeSMDMOGzWlc5yJrMN1d04osXN4sSfpo4O+azkBNBes0jg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.0.tgz", + "integrity": "sha512-kicDo0A/5J0nrsCPbn89mTG3Bm4XgYi0CZtvex9Oyw7gGZE3HXGD0zpQNH+mo+tEfbo8wbmMvJftOwpmPy7aVw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.0.tgz", + "integrity": "sha512-Y4rFpkZODfHrVo70Uaj6cC1JJOt3Pp0MdWSwIKtb8z1/lsjl9AmnB7ErRFV+QNGIfcY1Eruc2UMx5KaRnXjMyg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.0.tgz", + "integrity": "sha512-IvHmcTHDFztQGnn6aWq4t12QaBXTKr1whF/dgp9kz84X6GUcwq9utj7z2wFCUfeOup/QKnOlt2k0zxkGFx9ubg==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.0.tgz", + "integrity": "sha512-3jQUr/HBbMVZmi72LpjQwlZ55i1queL8KcDTQEkAHihttJnAPrcvG9ZNXIfsd2ugpizZo595egYV6xy+pv4Ofw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-create-class-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.0.tgz", + "integrity": "sha512-ti7IdM54NXv29cA4+bNNKEMS4jLMCbJgl+Drv+FgYy0erJLAxNAIXcNjNjrRZEcWq0xJHsNVwQezskMFpF8N9g==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.0.tgz", + "integrity": "sha512-vIFb5250Rbh7roWARvCLvIJ/PtAU5Lhv7BtZ1u24COwpI9Ypjsh+bZcKk6rlIyalK+r0jOc1XQ8I4ovNxNrWrA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.0.tgz", + "integrity": "sha512-PbIr7G9kR8tdH6g8Wouir5uVjklETk91GMVSUq+VaOgiinbCkBP6Q7NN/suM/QutZkMJMvcyAriogcYAdhg8Gw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.0.tgz", + "integrity": "sha512-V14As3haUOP4ZWrLJ3VVx5rCnrYhMSHN/jX7z6FAt5hjRkLsb0snPCmJwSOML5oxkKO4FNoNv7V5hw/y2bjuvg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.0.tgz", + "integrity": "sha512-27n3l67/R3UrXfizlvHGuTwsRIFyce3D/6a37GRxn28iyTPvNXaW4XvznexRh1zUNLPjbLL22Id0XQElV94ruw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.0.tgz", + "integrity": "sha512-HUxMvy6GtAdd+GKBNYDWCIA776byUQH8zjnfjxwT1P1ARv/wFu8eBDpmXQcLS/IwRtrxIReGiplOwMeyO7nsDQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.0.tgz", + "integrity": "sha512-63l1dRXday6S8V3WFY5mXJwcRAnPYxvFfTlt67bwV1rTyVTM5zrp0DBBb13Kl7+ehkCVwIZPumPpFP/4u70+Tw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.0.tgz", + "integrity": "sha512-Q7tBUwjxLTsHEoqktemHBMtb3NYwyJPTJdM+wDwb0g8PZ3kQUIzNvwD5lPaqW/p54TXBc/MXZu9Jr7tbUEUM8Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.0.tgz", + "integrity": "sha512-FXlDZfQeLILfJlC6I1qyEwcHK5UpRCFkaoVyA1nk9A1L1Yu583YO4un2KsLBsu3IJb4CUbctZks8tD9xPQubLw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.0.tgz", + "integrity": "sha512-LIe2kcHKAZOJDNxujvmp6z3mfN6V9lJxubU4fJIGoQCkKe3Ec2OcbdlYP+vW++4MpxwG0d1wSDOJtQW5kLnkZQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.0.tgz", + "integrity": "sha512-OwYEvzFI38hXklsrbNivzpO3fh87skzx8Pnqi4LoSYeav0xHlueSoCJrSgTPfnbyzopo5b3YVAJkFIcUpK2wsw==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.0.tgz", + "integrity": "sha512-5QKUw2kO+GVmKr2wMYSATCTTnHyscl6sxFRAY+rvN7h7WB0lcG0o4NoV6ZQU32OZGVsYUsfLGgPQpDFdkfjlJQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.0.tgz", + "integrity": "sha512-lBzMle9jcOXtSOXUpc7tvvTpENu/NuekNJVova5lCCWCV9/U1ho2HH2y0p6mBg8fPm/syEAbfaaemYGOHCY3mg==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.0.tgz", + "integrity": "sha512-gQDlsSF1iv9RU04clgXqRjrPyyoJMTclFt3K1cjLmTKikc0s/6vE3hlDeEVC71wLTRu72Fq7650kABrdTc2wMQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.0.tgz", + "integrity": "sha512-WRpw5HL4Jhnxw8QARzRvwojp9MIE7Tdk3ez6vRyUk1MwgjJN0aNpRoXainLR5SgxmoXx/vsXGZ6OthP6t/RbUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.0.tgz", + "integrity": "sha512-rWFhWbCJ9Wdmzln1NmSCqn7P0RAD+ogXG/bd9Kg5c7PKWkJtkiXmYsMBeXjDlzHpVTJ4I/hnjs45zX4dEv81xw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.0.tgz", + "integrity": "sha512-Dzi+NWqyEotgzk/sb7kgQPJQf7AJkQBWsVp1N6JWc1lBVo0vkElUnGdr1PzUBmfsCCN5OOFya3RtpeHk15oLKQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-simple-access": "^7.16.0", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.0.tgz", + "integrity": "sha512-yuGBaHS3lF1m/5R+6fjIke64ii5luRUg97N2wr+z1sF0V+sNSXPxXDdEEL/iYLszsN5VKxVB1IPfEqhzVpiqvg==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-identifier": "^7.15.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.0.tgz", + "integrity": "sha512-nx4f6no57himWiHhxDM5pjwhae5vLpTK2zCnDH8+wNLJy0TVER/LJRHl2bkt6w9Aad2sPD5iNNoUpY3X9sTGDg==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.0.tgz", + "integrity": "sha512-LogN88uO+7EhxWc8WZuQ8vxdSyVGxhkh8WTC3tzlT8LccMuQdA81e9SGV6zY7kY2LjDhhDOFdQVxdGwPyBCnvg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.0.tgz", + "integrity": "sha512-fhjrDEYv2DBsGN/P6rlqakwRwIp7rBGLPbrKxwh7oVt5NNkIhZVOY2GRV+ULLsQri1bDqwDWnU3vhlmx5B2aCw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.0.tgz", + "integrity": "sha512-fds+puedQHn4cPLshoHcR1DTMN0q1V9ou0mUjm8whx9pGcNvDrVVrgw+KJzzCaiTdaYhldtrUps8DWVMgrSEyg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-replace-supers": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.0.tgz", + "integrity": "sha512-XgnQEm1CevKROPx+udOi/8f8TiGhrUWiHiaUCIp47tE0tpFDjzXNTZc9E5CmCwxNjXTWEVqvRfWZYOTFvMa/ZQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.0.tgz", + "integrity": "sha512-XLldD4V8+pOqX2hwfWhgwXzGdnDOThxaNTgqagOcpBgIxbUvpgU2FMvo5E1RyHbk756WYgdbS0T8y0Cj9FKkWQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.0.tgz", + "integrity": "sha512-JAvGxgKuwS2PihiSFaDrp94XOzzTUeDeOQlcKzVAyaPap7BnZXK/lvMDiubkPTdotPKOIZq9xWXWnggUMYiExg==", + "dev": true, + "dependencies": { + "regenerator-transform": "^0.14.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.0.tgz", + "integrity": "sha512-Dgs8NNCehHSvXdhEhln8u/TtJxfVwGYCgP2OOr5Z3Ar+B+zXicEOKNTyc+eca2cuEOMtjW6m9P9ijOt8QdqWkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.0.tgz", + "integrity": "sha512-zlPf1/XFn5+vWdve3AAhf+Sxl+MVa5VlwTwWgnLx23u4GlatSRQJ3Eoo9vllf0a9il3woQsT4SK+5Z7c06h8ag==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "babel-plugin-polyfill-corejs2": "^0.2.3", + "babel-plugin-polyfill-corejs3": "^0.3.0", + "babel-plugin-polyfill-regenerator": "^0.2.3", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.0.tgz", + "integrity": "sha512-iVb1mTcD8fuhSv3k99+5tlXu5N0v8/DPm2mO3WACLG6al1CGZH7v09HJyUb1TtYl/Z+KrM6pHSIJdZxP5A+xow==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.0.tgz", + "integrity": "sha512-Ao4MSYRaLAQczZVp9/7E7QHsCuK92yHRrmVNRe/SlEJjhzivq0BSn8mEraimL8wizHZ3fuaHxKH0iwzI13GyGg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.0.tgz", + "integrity": "sha512-/ntT2NljR9foobKk4E/YyOSwcGUXtYWv5tinMK/3RkypyNBNdhHUaq6Orw5DWq9ZcNlS03BIlEALFeQgeVAo4Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.0.tgz", + "integrity": "sha512-Rd4Ic89hA/f7xUSJQk5PnC+4so50vBoBfxjdQAdvngwidM8jYIBVxBZ/sARxD4e0yMXRbJVDrYf7dyRtIIKT6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.0.tgz", + "integrity": "sha512-++V2L8Bdf4vcaHi2raILnptTBjGEFxn5315YU+e8+EqXIucA+q349qWngCLpUYqqv233suJ6NOienIVUpS9cqg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.0.tgz", + "integrity": "sha512-VFi4dhgJM7Bpk8lRc5CMaRGlKZ29W9C3geZjt9beuzSUrlJxsNwX7ReLwaL6WEvsOf2EQkyIJEPtF8EXjB/g2A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.0.tgz", + "integrity": "sha512-jHLK4LxhHjvCeZDWyA9c+P9XH1sOxRd1RO9xMtDVRAOND/PczPqizEtVdx4TQF/wyPaewqpT+tgQFYMnN/P94A==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.0.tgz", + "integrity": "sha512-cdTu/W0IrviamtnZiTfixPfIncr2M1VqRrkjzZWlr1B4TVYimCFK5jkyOdP4qw2MrlKHi+b3ORj6x8GoCew8Dg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.0", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-async-generator-functions": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-class-static-block": "^7.16.0", + "@babel/plugin-proposal-dynamic-import": "^7.16.0", + "@babel/plugin-proposal-export-namespace-from": "^7.16.0", + "@babel/plugin-proposal-json-strings": "^7.16.0", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.0", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-object-rest-spread": "^7.16.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-proposal-private-property-in-object": "^7.16.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.0", + "@babel/plugin-transform-async-to-generator": "^7.16.0", + "@babel/plugin-transform-block-scoped-functions": "^7.16.0", + "@babel/plugin-transform-block-scoping": "^7.16.0", + "@babel/plugin-transform-classes": "^7.16.0", + "@babel/plugin-transform-computed-properties": "^7.16.0", + "@babel/plugin-transform-destructuring": "^7.16.0", + "@babel/plugin-transform-dotall-regex": "^7.16.0", + "@babel/plugin-transform-duplicate-keys": "^7.16.0", + "@babel/plugin-transform-exponentiation-operator": "^7.16.0", + "@babel/plugin-transform-for-of": "^7.16.0", + "@babel/plugin-transform-function-name": "^7.16.0", + "@babel/plugin-transform-literals": "^7.16.0", + "@babel/plugin-transform-member-expression-literals": "^7.16.0", + "@babel/plugin-transform-modules-amd": "^7.16.0", + "@babel/plugin-transform-modules-commonjs": "^7.16.0", + "@babel/plugin-transform-modules-systemjs": "^7.16.0", + "@babel/plugin-transform-modules-umd": "^7.16.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.0", + "@babel/plugin-transform-new-target": "^7.16.0", + "@babel/plugin-transform-object-super": "^7.16.0", + "@babel/plugin-transform-parameters": "^7.16.0", + "@babel/plugin-transform-property-literals": "^7.16.0", + "@babel/plugin-transform-regenerator": "^7.16.0", + "@babel/plugin-transform-reserved-words": "^7.16.0", + "@babel/plugin-transform-shorthand-properties": "^7.16.0", + "@babel/plugin-transform-spread": "^7.16.0", + "@babel/plugin-transform-sticky-regex": "^7.16.0", + "@babel/plugin-transform-template-literals": "^7.16.0", + "@babel/plugin-transform-typeof-symbol": "^7.16.0", + "@babel/plugin-transform-unicode-escapes": "^7.16.0", + "@babel/plugin-transform-unicode-regex": "^7.16.0", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.0", + "babel-plugin-polyfill-corejs2": "^0.2.3", + "babel-plugin-polyfill-corejs3": "^0.3.0", + "babel-plugin-polyfill-regenerator": "^0.2.3", + "core-js-compat": "^3.19.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz", + "integrity": "sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", + "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.0.tgz", + "integrity": "sha512-qQ84jIs1aRQxaGaxSysII9TuDaguZ5yVrEuC0BN2vcPlalwfLovVmCjbFDPECPXcYM/wLvNFfp8uDOliLxIoUQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.15.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz", + "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.16.tgz", + "integrity": "sha512-EAEHtisTMM+KaKwfWdC3oyllIqswlznXCIVCt7/oRNrh+DhgT4UEBNC/jlADNjvw7UnfbcdkGQcPVZ1xYiLcrQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz", + "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/clean-css": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.5.tgz", + "integrity": "sha512-NEzjkGGpbs9S9fgC4abuBvTpVwE3i+Acu9BBod3PUyjDVZcNsGx61b8r2PphR61QGPnn0JHVs5ey6/I4eTrkxw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "source-map": "^0.6.0" + } + }, + "node_modules/@types/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@types/eslint": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz", + "integrity": "sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/http-proxy": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz", + "integrity": "sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/imagemin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/imagemin/-/imagemin-8.0.0.tgz", + "integrity": "sha512-B9X2CUeDv/uUeY9CqkzSTfmsLkeJP6PkmXlh4lODBbf9SwpmNuLS30WzUOi863dgsjY3zt3gY5q2F+UdifRi1A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/imagemin-gifsicle": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@types/imagemin-gifsicle/-/imagemin-gifsicle-7.0.1.tgz", + "integrity": "sha512-kUz6sUh0P95JOS0RGEaaemWUrASuw+dLsWIveK2UZJx74id/B9epgblMkCk/r5MjUWbZ83wFvacG5Rb/f97gyA==", + "dev": true, + "dependencies": { + "@types/imagemin": "*" + } + }, + "node_modules/@types/imagemin-mozjpeg": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@types/imagemin-mozjpeg/-/imagemin-mozjpeg-8.0.1.tgz", + "integrity": "sha512-kMQWEoKxxhlnH4POI3qfW9DjXlQfi80ux3l2b3j5R3eudSCoUIzKQLkfMjNJ6eMYnMWBcB+rfQOWqIzdIwFGKw==", + "dev": true, + "dependencies": { + "@types/imagemin": "*" + } + }, + "node_modules/@types/imagemin-optipng": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@types/imagemin-optipng/-/imagemin-optipng-5.2.1.tgz", + "integrity": "sha512-XCM/3q+HUL7v4zOqMI+dJ5dTxT+MUukY9KU49DSnYb/4yWtSMHJyADP+WHSMVzTR63J2ZvfUOzSilzBNEQW78g==", + "dev": true, + "dependencies": { + "@types/imagemin": "*" + } + }, + "node_modules/@types/imagemin-svgo": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@types/imagemin-svgo/-/imagemin-svgo-8.0.1.tgz", + "integrity": "sha512-YafkdrVAcr38U0Ln1C+L1n4SIZqC47VBHTyxCq7gTUSd1R9MdIvMcrljWlgU1M9O68WZDeQWUrKipKYfEOCOvQ==", + "dev": true, + "dependencies": { + "@types/imagemin": "*", + "@types/svgo": "^1" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", + "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", + "dev": true + }, + "node_modules/@types/svgo": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@types/svgo/-/svgo-1.3.6.tgz", + "integrity": "sha512-AZU7vQcy/4WFEuwnwsNsJnFwupIpbllH1++LXScN6uxT1Z4zPzdrWG97w4/I7eFKFTvfy/bHFStWjdBAg2Vjug==", + "dev": true + }, + "node_modules/@vue/component-compiler-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", + "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", + "dev": true, + "dependencies": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.36", + "postcss-selector-parser": "^6.0.2", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/@vue/component-compiler-utils/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", + "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", + "dev": true, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x", + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz", + "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==", + "dev": true, + "dependencies": { + "envinfo": "^7.7.3" + }, + "peerDependencies": { + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", + "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", + "dev": true, + "peerDependencies": { + "webpack-cli": "4.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz", + "integrity": "sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arity-n": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", + "integrity": "sha1-2edrEXM+CFacCEeuezmyhgswt0U=", + "dev": true + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.0.tgz", + "integrity": "sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA==", + "dev": true, + "dependencies": { + "browserslist": "^4.17.5", + "caniuse-lite": "^1.0.30001272", + "fraction.js": "^4.1.1", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/babel-loader": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", + "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.3.tgz", + "integrity": "sha512-NDZ0auNRzmAfE1oDDPW2JhzIMXUk+FFe2ICejmt5T4ocKgiQx3e0VCRx9NCAidcMtL2RUZaWtXnmjTCkx0tcbA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.2.4", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.3.0.tgz", + "integrity": "sha512-JLwi9vloVdXLjzACL80j24bG6/T1gYxwowG44dg6HN/7aTPdyPbJJidf6ajoA3RPHHtW0j9KMrSOLpIZpAnPpg==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.2.4", + "core-js-compat": "^3.18.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.3.tgz", + "integrity": "sha512-JVE78oRZPKFIeUqFGrSORNzQnrDwZR16oiWeGM8ZyjBn2XAT5OjP+wXx5ESuo33nUsFUEJYjtklnsKbxW5L+7g==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.2.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-sign/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.5.tgz", + "integrity": "sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001271", + "electron-to-chromium": "^1.3.878", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001274", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz", + "integrity": "sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", + "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", + "dev": true + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/clean-css": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz", + "integrity": "sha512-/eR8ru5zyxKzpBLv9YZvMXgTSSQn7AdkMItMYynsFgGwTveCRVam9IUPFloE85B4vAIj05IuKmmEoV7/AQjT0w==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/clean-webpack-plugin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==", + "dev": true, + "dependencies": { + "del": "^4.1.1" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.0 <6.0.0" + } + }, + "node_modules/cli-table3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", + "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/collect.js": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/collect.js/-/collect.js-4.29.0.tgz", + "integrity": "sha512-yhgGYEsLEcqnLT1NmRlN1+1euoz9SDhxQ4QyDhWYsKoWsg7252PKA5++dWaDs8mdFxbkmXDXQUaHXI9J2eTPkQ==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colord": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.1.tgz", + "integrity": "sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/compose-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", + "integrity": "sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=", + "dev": true, + "dependencies": { + "arity-n": "^1.0.4" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/concat": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/concat/-/concat-1.0.3.tgz", + "integrity": "sha1-QPM1MInWVGdpXLGIa0Xt1jfYzKg=", + "dev": true, + "dependencies": { + "commander": "^2.9.0" + }, + "bin": { + "concat": "bin/concat" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/concat/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", + "dev": true + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "node_modules/consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "dev": true, + "dependencies": { + "bluebird": "^3.1.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/core-js": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.0.tgz", + "integrity": "sha512-L1TpFRWXZ76vH1yLM+z6KssLZrP8Z6GxxW4auoCj+XiViOzNPJCAuTIkn03BGdFe6Z5clX5t64wRIRypsZQrUg==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.0.tgz", + "integrity": "sha512-R09rKZ56ccGBebjTLZHvzDxhz93YPT37gBm6qUhnwj3Kt7aCjjZWD1injyNbyeFHxNKfeZBSyds6O9n3MKq1sw==", + "dev": true, + "dependencies": { + "browserslist": "^4.17.5", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, + "node_modules/css-color-names": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", + "integrity": "sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.3.tgz", + "integrity": "sha512-SvjQjNRZgh4ULK1LDJ2AduPKUKxIqmtU7ZAyi47BTV+M90Qvxr9AB6lKlLbDUfXqI9IQeYA8LbAsCZPpJEV3aA==", + "dev": true, + "dependencies": { + "timsort": "^0.3.0" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-select": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", + "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^5.0.0", + "domhandler": "^4.2.0", + "domutils": "^2.6.0", + "nth-check": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select/node_modules/domhandler": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", + "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-what": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.9.tgz", + "integrity": "sha512-Y4olTKBKsPKl5izpcXHRDiB/1rVdbIDM4qVXgEKBt466kYT42SEEsnCYOQFFXzEkUYV8pJNCII9JKzb8KfDk+g==", + "dev": true, + "dependencies": { + "cssnano-preset-default": "^5.1.5", + "is-resolvable": "^1.1.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.5.tgz", + "integrity": "sha512-fF00UI+d3PWkGfMd62geqmoUe5h+LOhGE2GH4Fqq3beNKdCU1LWwLUyIcu4/A72lWv0737cHey5zhhWw3rW0sA==", + "dev": true, + "dependencies": { + "css-declaration-sorter": "^6.0.3", + "cssnano-utils": "^2.0.1", + "postcss-calc": "^8.0.0", + "postcss-colormin": "^5.2.1", + "postcss-convert-values": "^5.0.2", + "postcss-discard-comments": "^5.0.1", + "postcss-discard-duplicates": "^5.0.1", + "postcss-discard-empty": "^5.0.1", + "postcss-discard-overridden": "^5.0.1", + "postcss-merge-longhand": "^5.0.2", + "postcss-merge-rules": "^5.0.2", + "postcss-minify-font-values": "^5.0.1", + "postcss-minify-gradients": "^5.0.3", + "postcss-minify-params": "^5.0.1", + "postcss-minify-selectors": "^5.1.0", + "postcss-normalize-charset": "^5.0.1", + "postcss-normalize-display-values": "^5.0.1", + "postcss-normalize-positions": "^5.0.1", + "postcss-normalize-repeat-style": "^5.0.1", + "postcss-normalize-string": "^5.0.1", + "postcss-normalize-timing-functions": "^5.0.1", + "postcss-normalize-unicode": "^5.0.1", + "postcss-normalize-url": "^5.0.2", + "postcss-normalize-whitespace": "^5.0.1", + "postcss-ordered-values": "^5.0.2", + "postcss-reduce-initial": "^5.0.1", + "postcss-reduce-transforms": "^5.0.1", + "postcss-svgo": "^5.0.3", + "postcss-unique-selectors": "^5.0.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-discard-comments": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz", + "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.1.tgz", + "integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", + "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dom7": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dom7/-/dom7-3.0.0.tgz", + "integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==", + "dev": true, + "dependencies": { + "ssr-window": "^3.0.0-alpha.1" + } + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/domutils/node_modules/domhandler": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", + "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.3.886", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.886.tgz", + "integrity": "sha512-+vYdeBosI63VkCtNWnEVFjgNd/IZwvnsWkKyPtWAvrhA+XfByKoBJcbsMgudVU/bUcGAF9Xp3aXn96voWlc3oQ==", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", + "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "node_modules/es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dev": true, + "dependencies": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "dev": true, + "dependencies": { + "type": "^2.5.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/file-type": { + "version": "12.4.2", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", + "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", + "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", + "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash-base/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", + "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", + "dev": true + }, + "node_modules/html-loader": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-1.3.2.tgz", + "integrity": "sha512-DEkUwSd0sijK5PF3kRWspYi56XP7bTNkyg5YWSzBdjaSDmvCufep5c4Vpb3PBf6lUL0YPtLwBfy9fL0t5hBAGA==", + "dev": true, + "dependencies": { + "html-minifier-terser": "^5.1.1", + "htmlparser2": "^4.1.0", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/html-loader/node_modules/loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/html-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/html-minifier-terser/node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/html-minifier-terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/html-minifier-terser/node_modules/terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/html-minifier-terser/node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/htmlparser2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/http-parser-js": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz", + "integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.5", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/imagemin": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/imagemin/-/imagemin-7.0.1.tgz", + "integrity": "sha512-33AmZ+xjZhg2JMCe+vDf6a9mzWukE7l+wAtesjE7KyteqqKjzxv7aVQeWnul1Ve26mWvEQqyPwl0OctNBfSR9w==", + "dev": true, + "dependencies": { + "file-type": "^12.0.0", + "globby": "^10.0.0", + "graceful-fs": "^4.2.2", + "junk": "^3.1.0", + "make-dir": "^3.0.0", + "p-pipe": "^3.0.0", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imagemin/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/imagemin/node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/img-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/img-loader/-/img-loader-4.0.0.tgz", + "integrity": "sha512-UwRcPQdwdOyEHyCxe1V9s9YFwInwEWCpoO+kJGfIqDrBDqA8jZUsEZTxQ0JteNPGw/Gupmwesk2OhLTcnw6tnQ==", + "dev": true, + "dependencies": { + "loader-utils": "^1.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "imagemin": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "dependencies": { + "import-from": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", + "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-ip": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", + "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", + "dev": true, + "dependencies": { + "default-gateway": "^6.0.0", + "ipaddr.js": "^1.9.1", + "is-ip": "^3.1.0", + "p-event": "^4.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/internal-ip?sponsor=1" + } + }, + "node_modules/internal-ip/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dev": true, + "dependencies": { + "ip-regex": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.3.1.tgz", + "integrity": "sha512-ks3WCzsiZaOPJl/oMsDjaf0TRiSv7ctNgs0FqRr2nARsovz6AWWy4oLElwcquGSz692DzgZQrCLScPNs5YlC4g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/laravel-mix": { + "version": "6.0.37", + "resolved": "https://registry.npmjs.org/laravel-mix/-/laravel-mix-6.0.37.tgz", + "integrity": "sha512-mue45XrV7nGGvueP7b5mPlTsT3PUDiaTov1B11OnIr+BzaJXMMmf/r3qLRB4dmbXb5WgoQdk/3tVxZ49b2prbQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.15.8", + "@babel/plugin-proposal-object-rest-spread": "^7.15.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.15.8", + "@babel/preset-env": "^7.15.8", + "@babel/runtime": "^7.15.4", + "@types/babel__core": "^7.1.16", + "@types/clean-css": "^4.2.5", + "@types/imagemin-gifsicle": "^7.0.1", + "@types/imagemin-mozjpeg": "^8.0.1", + "@types/imagemin-optipng": "^5.2.1", + "@types/imagemin-svgo": "^8.0.0", + "autoprefixer": "^10.4.0", + "babel-loader": "^8.2.3", + "chalk": "^4.1.2", + "chokidar": "^3.5.2", + "clean-css": "^4.2.3 || ^5.1.2", + "cli-table3": "^0.6.0", + "collect.js": "^4.28.5", + "commander": "^7.2.0", + "concat": "^1.0.3", + "css-loader": "^5.2.6", + "cssnano": "^5.0.8", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "glob": "^7.2.0", + "html-loader": "^1.3.2", + "imagemin": "^7.0.1", + "img-loader": "^4.0.0", + "lodash": "^4.17.21", + "md5": "^2.3.0", + "mini-css-extract-plugin": "^1.6.2", + "node-libs-browser": "^2.2.1", + "postcss-load-config": "^3.1.0", + "postcss-loader": "^6.2.0", + "semver": "^7.3.5", + "strip-ansi": "^6.0.0", + "style-loader": "^2.0.0", + "terser": "^5.9.0", + "terser-webpack-plugin": "^5.2.4", + "vue-style-loader": "^4.1.3", + "webpack": "^5.60.0", + "webpack-cli": "^4.9.1", + "webpack-dev-server": "4.4.0", + "webpack-merge": "^5.8.0", + "webpack-notifier": "^1.14.1", + "webpackbar": "^5.0.0-3", + "yargs": "^17.2.1" + }, + "bin": { + "laravel-mix": "bin/cli.js", + "mix": "bin/cli.js" + }, + "engines": { + "node": ">=12.14.0" + }, + "peerDependencies": { + "@babel/core": "^7.15.8", + "@babel/plugin-proposal-object-rest-spread": "^7.15.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.15.8", + "@babel/preset-env": "^7.15.8", + "postcss": "^8.3.11", + "webpack": "^5.60.0", + "webpack-cli": "^4.9.1" + } + }, + "node_modules/laravel-mix-clean": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/laravel-mix-clean/-/laravel-mix-clean-0.1.0.tgz", + "integrity": "sha512-iOgVELzL0zJ2SNEVEmzCoduZ7tBQNWa/ViWXSTg9Z2QVK05Sld2efzTybR82Lu6ohZaA4btr+KsBBw7kt8aqTQ==", + "dev": true + }, + "node_modules/laravel-mix-polyfill": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/laravel-mix-polyfill/-/laravel-mix-polyfill-3.0.1.tgz", + "integrity": "sha512-05Oldnhek6jkg9JMl7Nf/qnkwuTOlwR7DLFWntfy98QOx5o/ipmnsayP9qVTxdLZNcBRuMz6Agt9fxZdO2AUJw==", + "dev": true, + "dependencies": { + "core-js": "^2.6.9 || ^3.1.4", + "regenerator-runtime": "^0.13.2" + }, + "peerDependencies": { + "laravel-mix": "^6.0.0" + } + }, + "node_modules/laravel-mix/node_modules/css-loader": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.27.0 || ^5.0.0" + } + }, + "node_modules/laravel-mix/node_modules/loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/laravel-mix/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/lilconfig": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", + "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/loader-utils/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.3.0.tgz", + "integrity": "sha512-BEE62uMfKOavX3iG7GYX43QJ+hAeeWnwIAuJ/R6q96jaMtiLzhsxHJC8B1L7fK7Pt/vXDRwb3SG/yBpNGDPqzg==", + "dev": true, + "dependencies": { + "fs-monkey": "1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "node_modules/merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/merge-source-map/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "dev": true, + "dependencies": { + "mime-db": "1.50.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz", + "integrity": "sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "webpack-sources": "^1.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.4.0 || ^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.1.30", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", + "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-notifier": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-9.0.1.tgz", + "integrity": "sha512-fPNFIp2hF/Dq7qLDzSg4vZ0J4e9v60gJR+Qx7RbjbWqzPDdEqeVpEx5CFeDAELIl+A/woaaNn1fQ5nEVerMxJg==", + "dev": true, + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "node_modules/node-notifier/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "node_modules/p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, + "dependencies": { + "p-timeout": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-pipe": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz", + "integrity": "sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", + "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", + "dev": true, + "dependencies": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/postcss": { + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", + "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", + "dev": true, + "dependencies": { + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", + "source-map-js": "^0.6.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-calc": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.0.0.tgz", + "integrity": "sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.1.tgz", + "integrity": "sha512-VVwMrEYLcHYePUYV99Ymuoi7WhKrMGy/V9/kTS0DkCoJYmmjdOMneyhzYUxcNgteKDVbrewOkSM7Wje/MFwxzA==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.2.tgz", + "integrity": "sha512-KQ04E2yadmfa1LqXm7UIDwW1ftxU/QWZmz6NKnHnUvJ3LEYbbcX6i329f/ig+WnEByHegulocXrECaZGLpL8Zg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-discard-comments/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-comments/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz", + "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz", + "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz", + "integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", + "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "dev": true, + "dependencies": { + "import-cwd": "^3.0.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.0.tgz", + "integrity": "sha512-H9hv447QjQJVDbHj3OUdciyAXY3v5+UDduzEytAlZCVHCpNAAg/mCSwhYYqZr9BiGYhmYspU8QXxZwiHTLn3yA==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.2.tgz", + "integrity": "sha512-BMlg9AXSI5G9TBT0Lo/H3PfUy63P84rVz3BjCFE9e9Y9RXQZD3+h3YO1kgTNsNJy7bBc1YQp8DmSnwLIW5VPcw==", + "dev": true, + "dependencies": { + "css-color-names": "^1.0.1", + "postcss-value-parser": "^4.1.0", + "stylehacks": "^5.0.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.2.tgz", + "integrity": "sha512-5K+Md7S3GwBewfB4rjDeol6V/RZ8S+v4B66Zk2gChRqLTCC8yjnHQ601omj9TKftS19OPGqZ/XzoqpzNQQLwbg==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^2.0.1", + "postcss-selector-parser": "^6.0.5", + "vendors": "^1.0.3" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.1.tgz", + "integrity": "sha512-7JS4qIsnqaxk+FXY1E8dHBDmraYFWmuL6cgt0T1SWGRO5bzJf8sUoelwa4P88LEWJZweHevAiDKxHlofuvtIoA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.3.tgz", + "integrity": "sha512-Z91Ol22nB6XJW+5oe31+YxRsYooxOdFKcbOqY/V8Fxse1Y3vqlNRpi1cxCqoACZTQEhl+xvt4hsbWiV5R+XI9Q==", + "dev": true, + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.1.tgz", + "integrity": "sha512-4RUC4k2A/Q9mGco1Z8ODc7h+A0z7L7X2ypO1B6V8057eVK6mZ6xwz6QN64nHuHLbqbclkX1wyzRnIrdZehTEHw==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.2", + "browserslist": "^4.16.0", + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.0.tgz", + "integrity": "sha512-NzGBXDa7aPsAcijXZeagnJBKBPMYLaJJzB8CQh6ncvyl2sIndLVWfbcDi0SBjRWk5VqEjXvf8tYwzoKf4Z07og==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.2", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz", + "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.1.tgz", + "integrity": "sha512-uupdvWk88kLDXi5HEyI9IaAJTE3/Djbcrqq8YgjvAVuzgVuqIk3SuJWUisT2gaJbZm1H9g5k2w1xXilM3x8DjQ==", + "dev": true, + "dependencies": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.1.tgz", + "integrity": "sha512-rvzWAJai5xej9yWqlCb1OWLd9JjW2Ex2BCPzUJrbaXmtKtgfL8dBMOOMTX6TnvQMtjk3ei1Lswcs78qKO1Skrg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.1.tgz", + "integrity": "sha512-syZ2itq0HTQjj4QtXZOeefomckiV5TaUO6ReIEabCh3wgDs4Mr01pkif0MeVwKyU/LHEkPJnpwFKRxqWA/7O3w==", + "dev": true, + "dependencies": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.1.tgz", + "integrity": "sha512-Ic8GaQ3jPMVl1OEn2U//2pm93AXUcF3wz+OriskdZ1AOuYV25OdgS7w9Xu2LO5cGyhHCgn8dMXh9bO7vi3i9pA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.1.tgz", + "integrity": "sha512-cPcBdVN5OsWCNEo5hiXfLUnXfTGtSFiBU9SK8k7ii8UD7OLuznzgNRYkLZow11BkQiiqMcgPyh4ZqXEEUrtQ1Q==", + "dev": true, + "dependencies": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.1.tgz", + "integrity": "sha512-kAtYD6V3pK0beqrU90gpCQB7g6AOfP/2KIPCVBKJM2EheVsBQmx/Iof+9zR9NFKLAx4Pr9mDhogB27pmn354nA==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.2.tgz", + "integrity": "sha512-k4jLTPUxREQ5bpajFQZpx8bCF2UrlqOTzP9kEqcEnOfwsRshWs2+oAFIHfDQB8GO2PaUaSE0NlTAYtbluZTlHQ==", + "dev": true, + "dependencies": { + "is-absolute-url": "^3.0.3", + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.1.tgz", + "integrity": "sha512-iPklmI5SBnRvwceb/XH568yyzK0qRVuAG+a1HFUsFRf11lEJTiQQa03a4RSCQvLKdcpX7XsI1Gen9LuLoqwiqA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.2.tgz", + "integrity": "sha512-8AFYDSOYWebJYLyJi3fyjl6CqMEG/UVworjiyK1r573I56kb3e879sCJLGvR3merj+fAdPpVplXKQZv+ey6CgQ==", + "dev": true, + "dependencies": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.1.tgz", + "integrity": "sha512-zlCZPKLLTMAqA3ZWH57HlbCjkD55LX9dsRyxlls+wfuRfqCi5mSlZVan0heX5cHr154Dq9AfbH70LyhrSAezJw==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.0", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.1.tgz", + "integrity": "sha512-a//FjoPeFkRuAguPscTVmRQUODP+f3ke2HqFNgGPwdYnpeC29RZdCBvGRGTsKpMURb/I3p6jdKoBQ2zI+9Q7kA==", + "dev": true, + "dependencies": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.3.tgz", + "integrity": "sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.1.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.1.tgz", + "integrity": "sha512-gwi1NhHV4FMmPn+qwBNuot1sG1t2OmacLQ/AX29lzyggnjd+MnVD5uqQmpXO3J17KGL2WAxQruj1qTd3H0gG/w==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.2", + "postcss-selector-parser": "^6.0.5", + "uniqs": "^2.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "node_modules/prettier": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", + "dev": true, + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz", + "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", + "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^9.0.0", + "regjsgen": "^0.5.2", + "regjsparser": "^0.7.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz", + "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/resolve-url-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.4.tgz", + "integrity": "sha512-D3sQ04o0eeQEySLrcz4DsX3saHfsr8/N6tfhblxgZKXxMT2Louargg12oGNfoTRLV09GXhVUe5/qgA5vdgNigg==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "3.0.0", + "camelcase": "5.3.1", + "compose-function": "3.0.3", + "convert-source-map": "1.7.0", + "es6-iterator": "2.0.3", + "loader-utils": "1.2.3", + "postcss": "7.0.36", + "rework": "1.0.1", + "rework-visit": "1.0.0", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/resolve-url-loader/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader/node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/resolve-url-loader/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/resolve-url-loader/node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/resolve-url-loader/node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/resolve-url-loader/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-url-loader/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rework": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", + "integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=", + "dev": true, + "dependencies": { + "convert-source-map": "^0.3.3", + "css": "^2.0.0" + } + }, + "node_modules/rework-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", + "integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=", + "dev": true + }, + "node_modules/rework/node_modules/convert-source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", + "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=", + "dev": true + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sass": { + "version": "1.43.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.43.4.tgz", + "integrity": "sha512-/ptG7KE9lxpGSYiXn7Ar+lKOv37xfWsZRtFYal2QHNigyVQDx685VFT/h7ejVr+R8w7H4tmUgtulsKl5YpveOg==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/sass-loader": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-11.1.1.tgz", + "integrity": "sha512-fOCp/zLmj1V1WHDZbUbPgrZhA7HKXHEqkslzB+05U5K9SbSbcmH91C7QLW31AsXikxUMaxXRhhcqWZAxUMLDyA==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0", + "sass": "^1.3.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "node_modules/selfsigned": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", + "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "dev": true, + "dependencies": { + "node-forge": "^0.10.0" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sockjs": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", + "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^3.4.0", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ssr-window": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz", + "integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==", + "dev": true + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/std-env": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-2.3.1.tgz", + "integrity": "sha512-eOsoKTWnr6C8aWrqJJ2KAReXoa7Vn5Ywyw6uCXgA/xDhxPoaIsBa5aNJmISY04dLwXPBnDHW4diGM7Sn5K4R/g==", + "dev": true, + "dependencies": { + "ci-info": "^3.1.1" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/style-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", + "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/style-loader/node_modules/loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/style-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/stylehacks": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz", + "integrity": "sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.0", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/swiper": { + "version": "6.8.4", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-6.8.4.tgz", + "integrity": "sha512-O+buF9Q+sMA0H7luMS8R59hCaJKlpo8PXhQ6ZYu6Rn2v9OsFd4d1jmrv14QvxtQpKAvL/ZiovEeANI/uDGet7g==", + "dev": true, + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/vladimirkharlampidi" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "hasInstallScript": true, + "dependencies": { + "dom7": "^3.0.0", + "ssr-window": "^3.0.0" + }, + "engines": { + "node": ">= 4.7.0" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.9.0.tgz", + "integrity": "sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz", + "integrity": "sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==", + "dev": true, + "dependencies": { + "jest-worker": "^27.0.6", + "p-limit": "^3.1.0", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/terser/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "node_modules/vue": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz", + "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==", + "dev": true + }, + "node_modules/vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true + }, + "node_modules/vue-loader": { + "version": "15.9.8", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.8.tgz", + "integrity": "sha512-GwSkxPrihfLR69/dSV3+5CdMQ0D+jXg8Ma1S4nQXKJAznYFX14vHdc/NetQc34Dw+rBbIJyP7JOuVb9Fhprvog==", + "dev": true, + "dependencies": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "peerDependencies": { + "css-loader": "*", + "webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "cache-loader": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/vue-style-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", + "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", + "dev": true, + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz", + "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==", + "dev": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "node_modules/vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "node_modules/watchpack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", + "integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webpack": { + "version": "5.61.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.61.0.tgz", + "integrity": "sha512-fPdTuaYZ/GMGFm4WrPi2KRCqS1vDp773kj9S0iI5Uc//5cszsFEDgHNaX4Rj1vobUiU1dFIV3mA9k1eHeluFpw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.2.0", + "webpack-sources": "^3.2.0" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz", + "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.1.0", + "@webpack-cli/info": "^1.4.0", + "@webpack-cli/serve": "^1.6.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "execa": "^5.0.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "@webpack-cli/migrate": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.2.1.tgz", + "integrity": "sha512-Kx1X+36Rn9JaZcQMrJ7qN3PMAuKmEDD9ZISjUj3Cgq4A6PtwYsC4mpaKotSRYH3iOF6HsUa8viHKS59FlyVifQ==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^3.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.4.0.tgz", + "integrity": "sha512-+S0XRIbsopVjPFjCO8I07FXYBWYqkFmuP56ucGMTs2hA/gV4q2M9xTmNo5Tg4o8ffRR+Nm3AsXnQXxKRyYovrA==", + "dev": true, + "dependencies": { + "ansi-html-community": "^0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^3.5.2", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "del": "^6.0.0", + "express": "^4.17.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "internal-ip": "^6.2.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^3.1.0", + "selfsigned": "^1.10.11", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^7.0.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^5.2.1", + "ws": "^8.1.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/webpack-dev-server/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-notifier": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/webpack-notifier/-/webpack-notifier-1.14.1.tgz", + "integrity": "sha512-OVOoiOyKHS3z9pN1nLdPY2Pf/R3wiBsN0KiPc3K6ApwMBfHbyUomQc2Mr0naeKxfqXyCBPHfQuqpL9yoL0rgkA==", + "dev": true, + "dependencies": { + "node-notifier": "^9.0.0", + "strip-ansi": "^6.0.0" + }, + "peerDependencies": { + "@types/webpack": "^4.41.31" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack/node_modules/webpack-sources": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.1.tgz", + "integrity": "sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpackbar": { + "version": "5.0.0-3", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.0-3.tgz", + "integrity": "sha512-viW6KCYjMb0NPoDrw2jAmLXU2dEOhRrtku28KmOfeE1vxbfwCYuTbTaMhnkrCZLFAFyY9Q49Z/jzYO80Dw5b8g==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.1.0", + "consola": "^2.15.0", + "figures": "^3.2.0", + "pretty-time": "^1.1.0", + "std-env": "^2.2.1", + "text-table": "^0.2.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "webpack": "3 || 4 || 5" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", + "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.0" + } + }, + "@babel/compat-data": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.0.tgz", + "integrity": "sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==", + "dev": true + }, + "@babel/core": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", + "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helpers": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", + "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz", + "integrity": "sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.0.tgz", + "integrity": "sha512-9KuleLT0e77wFUku6TUkqZzCEymBdtuQQ27MhEKzf9UOOJu3cYj98kyaDAzxpC7lV6DGiZFuC8XqDsq8/Kl6aQ==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.0.tgz", + "integrity": "sha512-S7iaOT1SYlqK0sQaCi21RX4+13hmdmnxIEAnQUB/eh7GeAnRjOUgTYpLkUOiRXzD+yog1JxP0qyAQZ7ZxVxLVg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.0.tgz", + "integrity": "sha512-XLwWvqEaq19zFlF5PTgOod4bUA+XbkR4WLQBct1bkzmxJGB0ZEJaoKF4c8cgH9oBtCDuYJ8BP5NB9uFiEgO5QA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-member-expression-to-functions": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.0.tgz", + "integrity": "sha512-3DyG0zAFAZKcOp7aVr33ddwkxJ0Z0Jr5V99y3I690eYLpukJsJvAbzTy1ewoCqsML8SbIrjH14Jc/nSQ4TvNPA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "regexpu-core": "^4.7.1" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.4.tgz", + "integrity": "sha512-OrpPZ97s+aPi6h2n1OXzdhVis1SGSsMU2aMHgLcOKfsp4/v1NWpx3CWT3lBj5eeBq9cDkPkh+YCfdF7O12uNDQ==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.0.tgz", + "integrity": "sha512-Hk2SLxC9ZbcOhLpg/yMznzJ11W++lg5GMbxt1ev6TXUiJB0N42KPC+7w8a+eWGuqDnUYuwStJoZHM7RgmIOaGQ==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-function-name": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", + "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", + "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", + "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", + "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", + "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-simple-access": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", + "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.0.tgz", + "integrity": "sha512-MLM1IOMe9aQBqMWxcRw8dcb9jlM86NIw7KA0Wri91Xkfied+dE0QuBFSBjMNvqzmS0OSIDsMNC24dBEkPUi7ew==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-wrap-function": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-replace-supers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", + "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", + "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", + "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.0.tgz", + "integrity": "sha512-VVMGzYY3vkWgCJML+qVLvGIam902mJW0FvT7Avj1zEe0Gn7D93aWdLblYARTxEw+6DhZmtzhBM2zv0ekE5zg1g==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/helpers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.0.tgz", + "integrity": "sha512-dVRM0StFMdKlkt7cVcGgwD8UMaBfWJHl3A83Yfs8GQ3MO0LHIIIMvK7Fa0RGOGUQ10qikLaX6D7o5htcQWgTMQ==", + "dev": true, + "requires": { + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/highlight": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.15.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.16.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.2.tgz", + "integrity": "sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==", + "dev": true + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.16.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.2.tgz", + "integrity": "sha512-h37CvpLSf8gb2lIJ2CgC3t+EjFbi0t8qS7LCS1xcJIlEXE4czlofwaW7W1HA8zpgOCzI9C1nmoqNR1zWkk0pQg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.0.tgz", + "integrity": "sha512-4tcFwwicpWTrpl9qjf7UsoosaArgImF85AxqCRZlgc3IQDvkUHjJpruXAL58Wmj+T6fypWTC/BakfEkwIL/pwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.0.tgz", + "integrity": "sha512-nyYmIo7ZqKsY6P4lnVmBlxp9B3a96CscbLotlsNuktMHahkDwoPYEjXrZHU0Tj844Z9f1IthVxQln57mhkcExw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.16.0", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.0.tgz", + "integrity": "sha512-mCF3HcuZSY9Fcx56Lbn+CGdT44ioBMMvjNVldpKtj8tpniETdLjnxdHI1+sDWXIM1nNt+EanJOZ3IG9lzVjs7A==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.0.tgz", + "integrity": "sha512-mAy3sdcY9sKAkf3lQbDiv3olOfiLqI51c9DR9b19uMoR2Z6r5pmGl7dfNFqEvqOyqbf1ta4lknK4gc5PJn3mfA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.0.tgz", + "integrity": "sha512-QGSA6ExWk95jFQgwz5GQ2Dr95cf7eI7TKutIXXTb7B1gCLTCz5hTjFTQGfLFBBiC5WSNi7udNwWsqbbMh1c4yQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.0.tgz", + "integrity": "sha512-CjI4nxM/D+5wCnhD11MHB1AwRSAYeDT+h8gCdcVJZ/OK7+wRzFsf7PFPWVpVpNRkHMmMkQWAHpTq+15IXQ1diA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.0.tgz", + "integrity": "sha512-kouIPuiv8mSi5JkEhzApg5Gn6hFyKPnlkO0a9YSzqRurH8wYzSlf6RJdzluAsbqecdW5pBvDJDfyDIUR/vLxvg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.0.tgz", + "integrity": "sha512-pbW0fE30sVTYXXm9lpVQQ/Vc+iTeQKiXlaNRZPPN2A2VdlWyAtsUrsQ3xydSlDW00TFMK7a8m3cDTkBF5WnV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.0.tgz", + "integrity": "sha512-3bnHA8CAFm7cG93v8loghDYyQ8r97Qydf63BeYiGgYbjKKB/XP53W15wfRC7dvKfoiJ34f6Rbyyx2btExc8XsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.0.tgz", + "integrity": "sha512-FAhE2I6mjispy+vwwd6xWPyEx3NYFS13pikDBWUAFGZvq6POGs5eNchw8+1CYoEgBl9n11I3NkzD7ghn25PQ9Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.0.tgz", + "integrity": "sha512-LU/+jp89efe5HuWJLmMmFG0+xbz+I2rSI7iLc1AlaeSMDMOGzWlc5yJrMN1d04osXN4sSfpo4O+azkBNBes0jg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.0" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.0.tgz", + "integrity": "sha512-kicDo0A/5J0nrsCPbn89mTG3Bm4XgYi0CZtvex9Oyw7gGZE3HXGD0zpQNH+mo+tEfbo8wbmMvJftOwpmPy7aVw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.0.tgz", + "integrity": "sha512-Y4rFpkZODfHrVo70Uaj6cC1JJOt3Pp0MdWSwIKtb8z1/lsjl9AmnB7ErRFV+QNGIfcY1Eruc2UMx5KaRnXjMyg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.0.tgz", + "integrity": "sha512-IvHmcTHDFztQGnn6aWq4t12QaBXTKr1whF/dgp9kz84X6GUcwq9utj7z2wFCUfeOup/QKnOlt2k0zxkGFx9ubg==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.0.tgz", + "integrity": "sha512-3jQUr/HBbMVZmi72LpjQwlZ55i1queL8KcDTQEkAHihttJnAPrcvG9ZNXIfsd2ugpizZo595egYV6xy+pv4Ofw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-create-class-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.0.tgz", + "integrity": "sha512-ti7IdM54NXv29cA4+bNNKEMS4jLMCbJgl+Drv+FgYy0erJLAxNAIXcNjNjrRZEcWq0xJHsNVwQezskMFpF8N9g==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.0.tgz", + "integrity": "sha512-vIFb5250Rbh7roWARvCLvIJ/PtAU5Lhv7BtZ1u24COwpI9Ypjsh+bZcKk6rlIyalK+r0jOc1XQ8I4ovNxNrWrA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.0.tgz", + "integrity": "sha512-PbIr7G9kR8tdH6g8Wouir5uVjklETk91GMVSUq+VaOgiinbCkBP6Q7NN/suM/QutZkMJMvcyAriogcYAdhg8Gw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.16.0" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.0.tgz", + "integrity": "sha512-V14As3haUOP4ZWrLJ3VVx5rCnrYhMSHN/jX7z6FAt5hjRkLsb0snPCmJwSOML5oxkKO4FNoNv7V5hw/y2bjuvg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.0.tgz", + "integrity": "sha512-27n3l67/R3UrXfizlvHGuTwsRIFyce3D/6a37GRxn28iyTPvNXaW4XvznexRh1zUNLPjbLL22Id0XQElV94ruw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.0.tgz", + "integrity": "sha512-HUxMvy6GtAdd+GKBNYDWCIA776byUQH8zjnfjxwT1P1ARv/wFu8eBDpmXQcLS/IwRtrxIReGiplOwMeyO7nsDQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-optimise-call-expression": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-replace-supers": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.0.tgz", + "integrity": "sha512-63l1dRXday6S8V3WFY5mXJwcRAnPYxvFfTlt67bwV1rTyVTM5zrp0DBBb13Kl7+ehkCVwIZPumPpFP/4u70+Tw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.0.tgz", + "integrity": "sha512-Q7tBUwjxLTsHEoqktemHBMtb3NYwyJPTJdM+wDwb0g8PZ3kQUIzNvwD5lPaqW/p54TXBc/MXZu9Jr7tbUEUM8Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.0.tgz", + "integrity": "sha512-FXlDZfQeLILfJlC6I1qyEwcHK5UpRCFkaoVyA1nk9A1L1Yu583YO4un2KsLBsu3IJb4CUbctZks8tD9xPQubLw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.0.tgz", + "integrity": "sha512-LIe2kcHKAZOJDNxujvmp6z3mfN6V9lJxubU4fJIGoQCkKe3Ec2OcbdlYP+vW++4MpxwG0d1wSDOJtQW5kLnkZQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.0.tgz", + "integrity": "sha512-OwYEvzFI38hXklsrbNivzpO3fh87skzx8Pnqi4LoSYeav0xHlueSoCJrSgTPfnbyzopo5b3YVAJkFIcUpK2wsw==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.0.tgz", + "integrity": "sha512-5QKUw2kO+GVmKr2wMYSATCTTnHyscl6sxFRAY+rvN7h7WB0lcG0o4NoV6ZQU32OZGVsYUsfLGgPQpDFdkfjlJQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.0.tgz", + "integrity": "sha512-lBzMle9jcOXtSOXUpc7tvvTpENu/NuekNJVova5lCCWCV9/U1ho2HH2y0p6mBg8fPm/syEAbfaaemYGOHCY3mg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.0.tgz", + "integrity": "sha512-gQDlsSF1iv9RU04clgXqRjrPyyoJMTclFt3K1cjLmTKikc0s/6vE3hlDeEVC71wLTRu72Fq7650kABrdTc2wMQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.0.tgz", + "integrity": "sha512-WRpw5HL4Jhnxw8QARzRvwojp9MIE7Tdk3ez6vRyUk1MwgjJN0aNpRoXainLR5SgxmoXx/vsXGZ6OthP6t/RbUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.0.tgz", + "integrity": "sha512-rWFhWbCJ9Wdmzln1NmSCqn7P0RAD+ogXG/bd9Kg5c7PKWkJtkiXmYsMBeXjDlzHpVTJ4I/hnjs45zX4dEv81xw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.0.tgz", + "integrity": "sha512-Dzi+NWqyEotgzk/sb7kgQPJQf7AJkQBWsVp1N6JWc1lBVo0vkElUnGdr1PzUBmfsCCN5OOFya3RtpeHk15oLKQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-simple-access": "^7.16.0", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.0.tgz", + "integrity": "sha512-yuGBaHS3lF1m/5R+6fjIke64ii5luRUg97N2wr+z1sF0V+sNSXPxXDdEEL/iYLszsN5VKxVB1IPfEqhzVpiqvg==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-identifier": "^7.15.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.0.tgz", + "integrity": "sha512-nx4f6no57himWiHhxDM5pjwhae5vLpTK2zCnDH8+wNLJy0TVER/LJRHl2bkt6w9Aad2sPD5iNNoUpY3X9sTGDg==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.0.tgz", + "integrity": "sha512-LogN88uO+7EhxWc8WZuQ8vxdSyVGxhkh8WTC3tzlT8LccMuQdA81e9SGV6zY7kY2LjDhhDOFdQVxdGwPyBCnvg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.0" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.0.tgz", + "integrity": "sha512-fhjrDEYv2DBsGN/P6rlqakwRwIp7rBGLPbrKxwh7oVt5NNkIhZVOY2GRV+ULLsQri1bDqwDWnU3vhlmx5B2aCw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.0.tgz", + "integrity": "sha512-fds+puedQHn4cPLshoHcR1DTMN0q1V9ou0mUjm8whx9pGcNvDrVVrgw+KJzzCaiTdaYhldtrUps8DWVMgrSEyg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-replace-supers": "^7.16.0" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.0.tgz", + "integrity": "sha512-XgnQEm1CevKROPx+udOi/8f8TiGhrUWiHiaUCIp47tE0tpFDjzXNTZc9E5CmCwxNjXTWEVqvRfWZYOTFvMa/ZQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.0.tgz", + "integrity": "sha512-XLldD4V8+pOqX2hwfWhgwXzGdnDOThxaNTgqagOcpBgIxbUvpgU2FMvo5E1RyHbk756WYgdbS0T8y0Cj9FKkWQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.0.tgz", + "integrity": "sha512-JAvGxgKuwS2PihiSFaDrp94XOzzTUeDeOQlcKzVAyaPap7BnZXK/lvMDiubkPTdotPKOIZq9xWXWnggUMYiExg==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.0.tgz", + "integrity": "sha512-Dgs8NNCehHSvXdhEhln8u/TtJxfVwGYCgP2OOr5Z3Ar+B+zXicEOKNTyc+eca2cuEOMtjW6m9P9ijOt8QdqWkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.0.tgz", + "integrity": "sha512-zlPf1/XFn5+vWdve3AAhf+Sxl+MVa5VlwTwWgnLx23u4GlatSRQJ3Eoo9vllf0a9il3woQsT4SK+5Z7c06h8ag==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "babel-plugin-polyfill-corejs2": "^0.2.3", + "babel-plugin-polyfill-corejs3": "^0.3.0", + "babel-plugin-polyfill-regenerator": "^0.2.3", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.0.tgz", + "integrity": "sha512-iVb1mTcD8fuhSv3k99+5tlXu5N0v8/DPm2mO3WACLG6al1CGZH7v09HJyUb1TtYl/Z+KrM6pHSIJdZxP5A+xow==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.0.tgz", + "integrity": "sha512-Ao4MSYRaLAQczZVp9/7E7QHsCuK92yHRrmVNRe/SlEJjhzivq0BSn8mEraimL8wizHZ3fuaHxKH0iwzI13GyGg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.0.tgz", + "integrity": "sha512-/ntT2NljR9foobKk4E/YyOSwcGUXtYWv5tinMK/3RkypyNBNdhHUaq6Orw5DWq9ZcNlS03BIlEALFeQgeVAo4Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.0.tgz", + "integrity": "sha512-Rd4Ic89hA/f7xUSJQk5PnC+4so50vBoBfxjdQAdvngwidM8jYIBVxBZ/sARxD4e0yMXRbJVDrYf7dyRtIIKT6Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.0.tgz", + "integrity": "sha512-++V2L8Bdf4vcaHi2raILnptTBjGEFxn5315YU+e8+EqXIucA+q349qWngCLpUYqqv233suJ6NOienIVUpS9cqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.0.tgz", + "integrity": "sha512-VFi4dhgJM7Bpk8lRc5CMaRGlKZ29W9C3geZjt9beuzSUrlJxsNwX7ReLwaL6WEvsOf2EQkyIJEPtF8EXjB/g2A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.0.tgz", + "integrity": "sha512-jHLK4LxhHjvCeZDWyA9c+P9XH1sOxRd1RO9xMtDVRAOND/PczPqizEtVdx4TQF/wyPaewqpT+tgQFYMnN/P94A==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/preset-env": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.0.tgz", + "integrity": "sha512-cdTu/W0IrviamtnZiTfixPfIncr2M1VqRrkjzZWlr1B4TVYimCFK5jkyOdP4qw2MrlKHi+b3ORj6x8GoCew8Dg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.0", + "@babel/helper-compilation-targets": "^7.16.0", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.0", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-async-generator-functions": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-class-static-block": "^7.16.0", + "@babel/plugin-proposal-dynamic-import": "^7.16.0", + "@babel/plugin-proposal-export-namespace-from": "^7.16.0", + "@babel/plugin-proposal-json-strings": "^7.16.0", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.0", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-object-rest-spread": "^7.16.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-proposal-private-property-in-object": "^7.16.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.0", + "@babel/plugin-transform-async-to-generator": "^7.16.0", + "@babel/plugin-transform-block-scoped-functions": "^7.16.0", + "@babel/plugin-transform-block-scoping": "^7.16.0", + "@babel/plugin-transform-classes": "^7.16.0", + "@babel/plugin-transform-computed-properties": "^7.16.0", + "@babel/plugin-transform-destructuring": "^7.16.0", + "@babel/plugin-transform-dotall-regex": "^7.16.0", + "@babel/plugin-transform-duplicate-keys": "^7.16.0", + "@babel/plugin-transform-exponentiation-operator": "^7.16.0", + "@babel/plugin-transform-for-of": "^7.16.0", + "@babel/plugin-transform-function-name": "^7.16.0", + "@babel/plugin-transform-literals": "^7.16.0", + "@babel/plugin-transform-member-expression-literals": "^7.16.0", + "@babel/plugin-transform-modules-amd": "^7.16.0", + "@babel/plugin-transform-modules-commonjs": "^7.16.0", + "@babel/plugin-transform-modules-systemjs": "^7.16.0", + "@babel/plugin-transform-modules-umd": "^7.16.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.0", + "@babel/plugin-transform-new-target": "^7.16.0", + "@babel/plugin-transform-object-super": "^7.16.0", + "@babel/plugin-transform-parameters": "^7.16.0", + "@babel/plugin-transform-property-literals": "^7.16.0", + "@babel/plugin-transform-regenerator": "^7.16.0", + "@babel/plugin-transform-reserved-words": "^7.16.0", + "@babel/plugin-transform-shorthand-properties": "^7.16.0", + "@babel/plugin-transform-spread": "^7.16.0", + "@babel/plugin-transform-sticky-regex": "^7.16.0", + "@babel/plugin-transform-template-literals": "^7.16.0", + "@babel/plugin-transform-typeof-symbol": "^7.16.0", + "@babel/plugin-transform-unicode-escapes": "^7.16.0", + "@babel/plugin-transform-unicode-regex": "^7.16.0", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.0", + "babel-plugin-polyfill-corejs2": "^0.2.3", + "babel-plugin-polyfill-corejs3": "^0.3.0", + "babel-plugin-polyfill-regenerator": "^0.2.3", + "core-js-compat": "^3.19.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz", + "integrity": "sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", + "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/traverse": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.0.tgz", + "integrity": "sha512-qQ84jIs1aRQxaGaxSysII9TuDaguZ5yVrEuC0BN2vcPlalwfLovVmCjbFDPECPXcYM/wLvNFfp8uDOliLxIoUQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.0", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.15.7", + "to-fast-properties": "^2.0.0" + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz", + "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true + }, + "@types/babel__core": { + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.16.tgz", + "integrity": "sha512-EAEHtisTMM+KaKwfWdC3oyllIqswlznXCIVCt7/oRNrh+DhgT4UEBNC/jlADNjvw7UnfbcdkGQcPVZ1xYiLcrQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz", + "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/clean-css": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.5.tgz", + "integrity": "sha512-NEzjkGGpbs9S9fgC4abuBvTpVwE3i+Acu9BBod3PUyjDVZcNsGx61b8r2PphR61QGPnn0JHVs5ey6/I4eTrkxw==", + "dev": true, + "requires": { + "@types/node": "*", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@types/eslint": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.2.tgz", + "integrity": "sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/http-proxy": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz", + "integrity": "sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/imagemin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/imagemin/-/imagemin-8.0.0.tgz", + "integrity": "sha512-B9X2CUeDv/uUeY9CqkzSTfmsLkeJP6PkmXlh4lODBbf9SwpmNuLS30WzUOi863dgsjY3zt3gY5q2F+UdifRi1A==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/imagemin-gifsicle": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@types/imagemin-gifsicle/-/imagemin-gifsicle-7.0.1.tgz", + "integrity": "sha512-kUz6sUh0P95JOS0RGEaaemWUrASuw+dLsWIveK2UZJx74id/B9epgblMkCk/r5MjUWbZ83wFvacG5Rb/f97gyA==", + "dev": true, + "requires": { + "@types/imagemin": "*" + } + }, + "@types/imagemin-mozjpeg": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@types/imagemin-mozjpeg/-/imagemin-mozjpeg-8.0.1.tgz", + "integrity": "sha512-kMQWEoKxxhlnH4POI3qfW9DjXlQfi80ux3l2b3j5R3eudSCoUIzKQLkfMjNJ6eMYnMWBcB+rfQOWqIzdIwFGKw==", + "dev": true, + "requires": { + "@types/imagemin": "*" + } + }, + "@types/imagemin-optipng": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@types/imagemin-optipng/-/imagemin-optipng-5.2.1.tgz", + "integrity": "sha512-XCM/3q+HUL7v4zOqMI+dJ5dTxT+MUukY9KU49DSnYb/4yWtSMHJyADP+WHSMVzTR63J2ZvfUOzSilzBNEQW78g==", + "dev": true, + "requires": { + "@types/imagemin": "*" + } + }, + "@types/imagemin-svgo": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@types/imagemin-svgo/-/imagemin-svgo-8.0.1.tgz", + "integrity": "sha512-YafkdrVAcr38U0Ln1C+L1n4SIZqC47VBHTyxCq7gTUSd1R9MdIvMcrljWlgU1M9O68WZDeQWUrKipKYfEOCOvQ==", + "dev": true, + "requires": { + "@types/imagemin": "*", + "@types/svgo": "^1" + } + }, + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "@types/node": { + "version": "16.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", + "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", + "dev": true + }, + "@types/svgo": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@types/svgo/-/svgo-1.3.6.tgz", + "integrity": "sha512-AZU7vQcy/4WFEuwnwsNsJnFwupIpbllH1++LXScN6uxT1Z4zPzdrWG97w4/I7eFKFTvfy/bHFStWjdBAg2Vjug==", + "dev": true + }, + "@vue/component-compiler-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", + "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", + "dev": true, + "requires": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.36", + "postcss-selector-parser": "^6.0.2", + "prettier": "^1.18.2 || ^2.0.0", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", + "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", + "dev": true + }, + "@webpack-cli/info": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz", + "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", + "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", + "dev": true + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", + "dev": true + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true + }, + "adjust-sourcemap-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz", + "integrity": "sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arity-n": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", + "integrity": "sha1-2edrEXM+CFacCEeuezmyhgswt0U=", + "dev": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.0.tgz", + "integrity": "sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA==", + "dev": true, + "requires": { + "browserslist": "^4.17.5", + "caniuse-lite": "^1.0.30001272", + "fraction.js": "^4.1.1", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.1.0" + } + }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "babel-loader": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", + "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.3.tgz", + "integrity": "sha512-NDZ0auNRzmAfE1oDDPW2JhzIMXUk+FFe2ICejmt5T4ocKgiQx3e0VCRx9NCAidcMtL2RUZaWtXnmjTCkx0tcbA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.2.4", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.3.0.tgz", + "integrity": "sha512-JLwi9vloVdXLjzACL80j24bG6/T1gYxwowG44dg6HN/7aTPdyPbJJidf6ajoA3RPHHtW0j9KMrSOLpIZpAnPpg==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.2.4", + "core-js-compat": "^3.18.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.3.tgz", + "integrity": "sha512-JVE78oRZPKFIeUqFGrSORNzQnrDwZR16oiWeGM8ZyjBn2XAT5OjP+wXx5ESuo33nUsFUEJYjtklnsKbxW5L+7g==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.2.4" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.5.tgz", + "integrity": "sha512-I3ekeB92mmpctWBoLXe0d5wPS2cBuRvvW0JyyJHMrk9/HmP2ZjrTboNAZ8iuGqaEIlKguljbQY32OkOJIRrgoA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001271", + "electron-to-chromium": "^1.3.878", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001274", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz", + "integrity": "sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", + "dev": true + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "ci-info": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", + "integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "clean-css": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz", + "integrity": "sha512-/eR8ru5zyxKzpBLv9YZvMXgTSSQn7AdkMItMYynsFgGwTveCRVam9IUPFloE85B4vAIj05IuKmmEoV7/AQjT0w==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "clean-webpack-plugin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==", + "dev": true, + "requires": { + "del": "^4.1.1" + } + }, + "cli-table3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", + "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "collect.js": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/collect.js/-/collect.js-4.29.0.tgz", + "integrity": "sha512-yhgGYEsLEcqnLT1NmRlN1+1euoz9SDhxQ4QyDhWYsKoWsg7252PKA5++dWaDs8mdFxbkmXDXQUaHXI9J2eTPkQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colord": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.1.tgz", + "integrity": "sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw==", + "dev": true + }, + "colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "optional": true + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compose-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", + "integrity": "sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=", + "dev": true, + "requires": { + "arity-n": "^1.0.4" + } + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "concat": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/concat/-/concat-1.0.3.tgz", + "integrity": "sha1-QPM1MInWVGdpXLGIa0Xt1jfYzKg=", + "dev": true, + "requires": { + "commander": "^2.9.0" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "dev": true, + "requires": { + "bluebird": "^3.1.1" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "core-js": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.0.tgz", + "integrity": "sha512-L1TpFRWXZ76vH1yLM+z6KssLZrP8Z6GxxW4auoCj+XiViOzNPJCAuTIkn03BGdFe6Z5clX5t64wRIRypsZQrUg==", + "dev": true + }, + "core-js-compat": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.0.tgz", + "integrity": "sha512-R09rKZ56ccGBebjTLZHvzDxhz93YPT37gBm6qUhnwj3Kt7aCjjZWD1injyNbyeFHxNKfeZBSyds6O9n3MKq1sw==", + "dev": true, + "requires": { + "browserslist": "^4.17.5", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", + "dev": true + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-color-names": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", + "integrity": "sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==", + "dev": true + }, + "css-declaration-sorter": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.3.tgz", + "integrity": "sha512-SvjQjNRZgh4ULK1LDJ2AduPKUKxIqmtU7ZAyi47BTV+M90Qvxr9AB6lKlLbDUfXqI9IQeYA8LbAsCZPpJEV3aA==", + "dev": true, + "requires": { + "timsort": "^0.3.0" + } + }, + "css-select": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", + "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^5.0.0", + "domhandler": "^4.2.0", + "domutils": "^2.6.0", + "nth-check": "^2.0.0" + }, + "dependencies": { + "domhandler": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", + "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + } + } + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-what": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssnano": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.9.tgz", + "integrity": "sha512-Y4olTKBKsPKl5izpcXHRDiB/1rVdbIDM4qVXgEKBt466kYT42SEEsnCYOQFFXzEkUYV8pJNCII9JKzb8KfDk+g==", + "dev": true, + "requires": { + "cssnano-preset-default": "^5.1.5", + "is-resolvable": "^1.1.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "cssnano-preset-default": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.5.tgz", + "integrity": "sha512-fF00UI+d3PWkGfMd62geqmoUe5h+LOhGE2GH4Fqq3beNKdCU1LWwLUyIcu4/A72lWv0737cHey5zhhWw3rW0sA==", + "dev": true, + "requires": { + "css-declaration-sorter": "^6.0.3", + "cssnano-utils": "^2.0.1", + "postcss-calc": "^8.0.0", + "postcss-colormin": "^5.2.1", + "postcss-convert-values": "^5.0.2", + "postcss-discard-comments": "^5.0.1", + "postcss-discard-duplicates": "^5.0.1", + "postcss-discard-empty": "^5.0.1", + "postcss-discard-overridden": "^5.0.1", + "postcss-merge-longhand": "^5.0.2", + "postcss-merge-rules": "^5.0.2", + "postcss-minify-font-values": "^5.0.1", + "postcss-minify-gradients": "^5.0.3", + "postcss-minify-params": "^5.0.1", + "postcss-minify-selectors": "^5.1.0", + "postcss-normalize-charset": "^5.0.1", + "postcss-normalize-display-values": "^5.0.1", + "postcss-normalize-positions": "^5.0.1", + "postcss-normalize-repeat-style": "^5.0.1", + "postcss-normalize-string": "^5.0.1", + "postcss-normalize-timing-functions": "^5.0.1", + "postcss-normalize-unicode": "^5.0.1", + "postcss-normalize-url": "^5.0.2", + "postcss-normalize-whitespace": "^5.0.1", + "postcss-ordered-values": "^5.0.2", + "postcss-reduce-initial": "^5.0.1", + "postcss-reduce-transforms": "^5.0.1", + "postcss-svgo": "^5.0.3", + "postcss-unique-selectors": "^5.0.1" + }, + "dependencies": { + "postcss-discard-comments": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz", + "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==", + "dev": true + } + } + }, + "cssnano-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.1.tgz", + "integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==", + "dev": true + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "requires": { + "css-tree": "^1.1.2" + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "dev": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "dependencies": { + "domhandler": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", + "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + } + } + }, + "dom7": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dom7/-/dom7-3.0.0.tgz", + "integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==", + "dev": true, + "requires": { + "ssr-window": "^3.0.0-alpha.1" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "dependencies": { + "domhandler": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.2.tgz", + "integrity": "sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + } + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.886", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.886.tgz", + "integrity": "sha512-+vYdeBosI63VkCtNWnEVFjgNd/IZwvnsWkKyPtWAvrhA+XfByKoBJcbsMgudVU/bUcGAF9Xp3aXn96voWlc3oQ==", + "dev": true + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "enhanced-resolve": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", + "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "dev": true, + "requires": { + "type": "^2.5.0" + }, + "dependencies": { + "type": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==", + "dev": true + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "file-type": { + "version": "12.4.2", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", + "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "follow-redirects": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", + "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "dev": true + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fraction.js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", + "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-entities": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", + "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", + "dev": true + }, + "html-loader": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-1.3.2.tgz", + "integrity": "sha512-DEkUwSd0sijK5PF3kRWspYi56XP7bTNkyg5YWSzBdjaSDmvCufep5c4Vpb3PBf6lUL0YPtLwBfy9fL0t5hBAGA==", + "dev": true, + "requires": { + "html-minifier-terser": "^5.1.1", + "htmlparser2": "^4.1.0", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "dev": true, + "requires": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + }, + "dependencies": { + "clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + } + } + }, + "htmlparser2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "http-parser-js": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", + "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz", + "integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.5", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "imagemin": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/imagemin/-/imagemin-7.0.1.tgz", + "integrity": "sha512-33AmZ+xjZhg2JMCe+vDf6a9mzWukE7l+wAtesjE7KyteqqKjzxv7aVQeWnul1Ve26mWvEQqyPwl0OctNBfSR9w==", + "dev": true, + "requires": { + "file-type": "^12.0.0", + "globby": "^10.0.0", + "graceful-fs": "^4.2.2", + "junk": "^3.1.0", + "make-dir": "^3.0.0", + "p-pipe": "^3.0.0", + "replace-ext": "^1.0.0" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + } + } + } + }, + "img-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/img-loader/-/img-loader-4.0.0.tgz", + "integrity": "sha512-UwRcPQdwdOyEHyCxe1V9s9YFwInwEWCpoO+kJGfIqDrBDqA8jZUsEZTxQ0JteNPGw/Gupmwesk2OhLTcnw6tnQ==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0" + } + }, + "import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "requires": { + "import-from": "^3.0.0" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "import-local": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", + "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "internal-ip": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", + "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", + "dev": true, + "requires": { + "default-gateway": "^6.0.0", + "ipaddr.js": "^1.9.1", + "is-ip": "^3.1.0", + "p-event": "^4.2.0" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } + } + }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "dev": true + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dev": true, + "requires": { + "ip-regex": "^4.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "jest-worker": { + "version": "27.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.3.1.tgz", + "integrity": "sha512-ks3WCzsiZaOPJl/oMsDjaf0TRiSv7ctNgs0FqRr2nARsovz6AWWy4oLElwcquGSz692DzgZQrCLScPNs5YlC4g==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true + }, + "laravel-mix": { + "version": "6.0.37", + "resolved": "https://registry.npmjs.org/laravel-mix/-/laravel-mix-6.0.37.tgz", + "integrity": "sha512-mue45XrV7nGGvueP7b5mPlTsT3PUDiaTov1B11OnIr+BzaJXMMmf/r3qLRB4dmbXb5WgoQdk/3tVxZ49b2prbQ==", + "dev": true, + "requires": { + "@babel/core": "^7.15.8", + "@babel/plugin-proposal-object-rest-spread": "^7.15.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.15.8", + "@babel/preset-env": "^7.15.8", + "@babel/runtime": "^7.15.4", + "@types/babel__core": "^7.1.16", + "@types/clean-css": "^4.2.5", + "@types/imagemin-gifsicle": "^7.0.1", + "@types/imagemin-mozjpeg": "^8.0.1", + "@types/imagemin-optipng": "^5.2.1", + "@types/imagemin-svgo": "^8.0.0", + "autoprefixer": "^10.4.0", + "babel-loader": "^8.2.3", + "chalk": "^4.1.2", + "chokidar": "^3.5.2", + "clean-css": "^4.2.3 || ^5.1.2", + "cli-table3": "^0.6.0", + "collect.js": "^4.28.5", + "commander": "^7.2.0", + "concat": "^1.0.3", + "css-loader": "^5.2.6", + "cssnano": "^5.0.8", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "glob": "^7.2.0", + "html-loader": "^1.3.2", + "imagemin": "^7.0.1", + "img-loader": "^4.0.0", + "lodash": "^4.17.21", + "md5": "^2.3.0", + "mini-css-extract-plugin": "^1.6.2", + "node-libs-browser": "^2.2.1", + "postcss-load-config": "^3.1.0", + "postcss-loader": "^6.2.0", + "semver": "^7.3.5", + "strip-ansi": "^6.0.0", + "style-loader": "^2.0.0", + "terser": "^5.9.0", + "terser-webpack-plugin": "^5.2.4", + "vue-style-loader": "^4.1.3", + "webpack": "^5.60.0", + "webpack-cli": "^4.9.1", + "webpack-dev-server": "4.4.0", + "webpack-merge": "^5.8.0", + "webpack-notifier": "^1.14.1", + "webpackbar": "^5.0.0-3", + "yargs": "^17.2.1" + }, + "dependencies": { + "css-loader": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.5" + } + }, + "loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "laravel-mix-clean": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/laravel-mix-clean/-/laravel-mix-clean-0.1.0.tgz", + "integrity": "sha512-iOgVELzL0zJ2SNEVEmzCoduZ7tBQNWa/ViWXSTg9Z2QVK05Sld2efzTybR82Lu6ohZaA4btr+KsBBw7kt8aqTQ==", + "dev": true + }, + "laravel-mix-polyfill": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/laravel-mix-polyfill/-/laravel-mix-polyfill-3.0.1.tgz", + "integrity": "sha512-05Oldnhek6jkg9JMl7Nf/qnkwuTOlwR7DLFWntfy98QOx5o/ipmnsayP9qVTxdLZNcBRuMz6Agt9fxZdO2AUJw==", + "dev": true, + "requires": { + "core-js": "^2.6.9 || ^3.1.4", + "regenerator-runtime": "^0.13.2" + } + }, + "lilconfig": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", + "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==", + "dev": true + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memfs": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.3.0.tgz", + "integrity": "sha512-BEE62uMfKOavX3iG7GYX43QJ+hAeeWnwIAuJ/R6q96jaMtiLzhsxHJC8B1L7fK7Pt/vXDRwb3SG/yBpNGDPqzg==", + "dev": true, + "requires": { + "fs-monkey": "1.0.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==", + "dev": true + }, + "mime-types": { + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "dev": true, + "requires": { + "mime-db": "1.50.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz", + "integrity": "sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "nanoid": { + "version": "3.1.30", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", + "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node-notifier": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-9.0.1.tgz", + "integrity": "sha512-fPNFIp2hF/Dq7qLDzSg4vZ0J4e9v60gJR+Qx7RbjbWqzPDdEqeVpEx5CFeDAELIl+A/woaaNn1fQ5nEVerMxJg==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + } + } + }, + "node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, + "requires": { + "p-timeout": "^3.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "p-pipe": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz", + "integrity": "sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==", + "dev": true + }, + "p-retry": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", + "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "postcss": { + "version": "8.3.11", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", + "integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", + "dev": true, + "requires": { + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", + "source-map-js": "^0.6.2" + } + }, + "postcss-calc": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.0.0.tgz", + "integrity": "sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "postcss-colormin": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.1.tgz", + "integrity": "sha512-VVwMrEYLcHYePUYV99Ymuoi7WhKrMGy/V9/kTS0DkCoJYmmjdOMneyhzYUxcNgteKDVbrewOkSM7Wje/MFwxzA==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-convert-values": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.2.tgz", + "integrity": "sha512-KQ04E2yadmfa1LqXm7UIDwW1ftxU/QWZmz6NKnHnUvJ3LEYbbcX6i329f/ig+WnEByHegulocXrECaZGLpL8Zg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-discard-duplicates": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz", + "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==", + "dev": true + }, + "postcss-discard-empty": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz", + "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==", + "dev": true + }, + "postcss-discard-overridden": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz", + "integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==", + "dev": true + }, + "postcss-load-config": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", + "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "dev": true, + "requires": { + "import-cwd": "^3.0.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "postcss-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.0.tgz", + "integrity": "sha512-H9hv447QjQJVDbHj3OUdciyAXY3v5+UDduzEytAlZCVHCpNAAg/mCSwhYYqZr9BiGYhmYspU8QXxZwiHTLn3yA==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "semver": "^7.3.5" + } + }, + "postcss-merge-longhand": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.2.tgz", + "integrity": "sha512-BMlg9AXSI5G9TBT0Lo/H3PfUy63P84rVz3BjCFE9e9Y9RXQZD3+h3YO1kgTNsNJy7bBc1YQp8DmSnwLIW5VPcw==", + "dev": true, + "requires": { + "css-color-names": "^1.0.1", + "postcss-value-parser": "^4.1.0", + "stylehacks": "^5.0.1" + } + }, + "postcss-merge-rules": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.2.tgz", + "integrity": "sha512-5K+Md7S3GwBewfB4rjDeol6V/RZ8S+v4B66Zk2gChRqLTCC8yjnHQ601omj9TKftS19OPGqZ/XzoqpzNQQLwbg==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^2.0.1", + "postcss-selector-parser": "^6.0.5", + "vendors": "^1.0.3" + } + }, + "postcss-minify-font-values": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.1.tgz", + "integrity": "sha512-7JS4qIsnqaxk+FXY1E8dHBDmraYFWmuL6cgt0T1SWGRO5bzJf8sUoelwa4P88LEWJZweHevAiDKxHlofuvtIoA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-minify-gradients": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.3.tgz", + "integrity": "sha512-Z91Ol22nB6XJW+5oe31+YxRsYooxOdFKcbOqY/V8Fxse1Y3vqlNRpi1cxCqoACZTQEhl+xvt4hsbWiV5R+XI9Q==", + "dev": true, + "requires": { + "colord": "^2.9.1", + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-minify-params": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.1.tgz", + "integrity": "sha512-4RUC4k2A/Q9mGco1Z8ODc7h+A0z7L7X2ypO1B6V8057eVK6mZ6xwz6QN64nHuHLbqbclkX1wyzRnIrdZehTEHw==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.2", + "browserslist": "^4.16.0", + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0", + "uniqs": "^2.0.0" + } + }, + "postcss-minify-selectors": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.0.tgz", + "integrity": "sha512-NzGBXDa7aPsAcijXZeagnJBKBPMYLaJJzB8CQh6ncvyl2sIndLVWfbcDi0SBjRWk5VqEjXvf8tYwzoKf4Z07og==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.2", + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-normalize-charset": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz", + "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==", + "dev": true + }, + "postcss-normalize-display-values": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.1.tgz", + "integrity": "sha512-uupdvWk88kLDXi5HEyI9IaAJTE3/Djbcrqq8YgjvAVuzgVuqIk3SuJWUisT2gaJbZm1H9g5k2w1xXilM3x8DjQ==", + "dev": true, + "requires": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-positions": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.1.tgz", + "integrity": "sha512-rvzWAJai5xej9yWqlCb1OWLd9JjW2Ex2BCPzUJrbaXmtKtgfL8dBMOOMTX6TnvQMtjk3ei1Lswcs78qKO1Skrg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.1.tgz", + "integrity": "sha512-syZ2itq0HTQjj4QtXZOeefomckiV5TaUO6ReIEabCh3wgDs4Mr01pkif0MeVwKyU/LHEkPJnpwFKRxqWA/7O3w==", + "dev": true, + "requires": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-string": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.1.tgz", + "integrity": "sha512-Ic8GaQ3jPMVl1OEn2U//2pm93AXUcF3wz+OriskdZ1AOuYV25OdgS7w9Xu2LO5cGyhHCgn8dMXh9bO7vi3i9pA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.1.tgz", + "integrity": "sha512-cPcBdVN5OsWCNEo5hiXfLUnXfTGtSFiBU9SK8k7ii8UD7OLuznzgNRYkLZow11BkQiiqMcgPyh4ZqXEEUrtQ1Q==", + "dev": true, + "requires": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-unicode": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.1.tgz", + "integrity": "sha512-kAtYD6V3pK0beqrU90gpCQB7g6AOfP/2KIPCVBKJM2EheVsBQmx/Iof+9zR9NFKLAx4Pr9mDhogB27pmn354nA==", + "dev": true, + "requires": { + "browserslist": "^4.16.0", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-url": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.2.tgz", + "integrity": "sha512-k4jLTPUxREQ5bpajFQZpx8bCF2UrlqOTzP9kEqcEnOfwsRshWs2+oAFIHfDQB8GO2PaUaSE0NlTAYtbluZTlHQ==", + "dev": true, + "requires": { + "is-absolute-url": "^3.0.3", + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-normalize-whitespace": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.1.tgz", + "integrity": "sha512-iPklmI5SBnRvwceb/XH568yyzK0qRVuAG+a1HFUsFRf11lEJTiQQa03a4RSCQvLKdcpX7XsI1Gen9LuLoqwiqA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-ordered-values": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.2.tgz", + "integrity": "sha512-8AFYDSOYWebJYLyJi3fyjl6CqMEG/UVworjiyK1r573I56kb3e879sCJLGvR3merj+fAdPpVplXKQZv+ey6CgQ==", + "dev": true, + "requires": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-reduce-initial": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.1.tgz", + "integrity": "sha512-zlCZPKLLTMAqA3ZWH57HlbCjkD55LX9dsRyxlls+wfuRfqCi5mSlZVan0heX5cHr154Dq9AfbH70LyhrSAezJw==", + "dev": true, + "requires": { + "browserslist": "^4.16.0", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.1.tgz", + "integrity": "sha512-a//FjoPeFkRuAguPscTVmRQUODP+f3ke2HqFNgGPwdYnpeC29RZdCBvGRGTsKpMURb/I3p6jdKoBQ2zI+9Q7kA==", + "dev": true, + "requires": { + "cssnano-utils": "^2.0.1", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.3.tgz", + "integrity": "sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.1.0", + "svgo": "^2.7.0" + } + }, + "postcss-unique-selectors": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.1.tgz", + "integrity": "sha512-gwi1NhHV4FMmPn+qwBNuot1sG1t2OmacLQ/AX29lzyggnjd+MnVD5uqQmpXO3J17KGL2WAxQruj1qTd3H0gG/w==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.2", + "postcss-selector-parser": "^6.0.5", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "prettier": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", + "dev": true, + "optional": true + }, + "pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz", + "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==", + "dev": true, + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "regexpu-core": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", + "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==", + "dev": true, + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^9.0.0", + "regjsgen": "^0.5.2", + "regjsparser": "^0.7.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true + }, + "regjsparser": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz", + "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "resolve-url-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.4.tgz", + "integrity": "sha512-D3sQ04o0eeQEySLrcz4DsX3saHfsr8/N6tfhblxgZKXxMT2Louargg12oGNfoTRLV09GXhVUe5/qgA5vdgNigg==", + "dev": true, + "requires": { + "adjust-sourcemap-loader": "3.0.0", + "camelcase": "5.3.1", + "compose-function": "3.0.3", + "convert-source-map": "1.7.0", + "es6-iterator": "2.0.3", + "loader-utils": "1.2.3", + "postcss": "7.0.36", + "rework": "1.0.1", + "rework-visit": "1.0.0", + "source-map": "0.6.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rework": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", + "integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=", + "dev": true, + "requires": { + "convert-source-map": "^0.3.3", + "css": "^2.0.0" + }, + "dependencies": { + "convert-source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", + "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=", + "dev": true + } + } + }, + "rework-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", + "integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sass": { + "version": "1.43.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.43.4.tgz", + "integrity": "sha512-/ptG7KE9lxpGSYiXn7Ar+lKOv37xfWsZRtFYal2QHNigyVQDx685VFT/h7ejVr+R8w7H4tmUgtulsKl5YpveOg==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0" + } + }, + "sass-loader": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-11.1.1.tgz", + "integrity": "sha512-fOCp/zLmj1V1WHDZbUbPgrZhA7HKXHEqkslzB+05U5K9SbSbcmH91C7QLW31AsXikxUMaxXRhhcqWZAxUMLDyA==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", + "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "dev": true, + "requires": { + "node-forge": "^0.10.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "sockjs": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", + "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^3.4.0", + "websocket-driver": "^0.7.4" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "ssr-window": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz", + "integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==", + "dev": true + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "std-env": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-2.3.1.tgz", + "integrity": "sha512-eOsoKTWnr6C8aWrqJJ2KAReXoa7Vn5Ywyw6uCXgA/xDhxPoaIsBa5aNJmISY04dLwXPBnDHW4diGM7Sn5K4R/g==", + "dev": true, + "requires": { + "ci-info": "^3.1.1" + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "style-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", + "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.1.tgz", + "integrity": "sha512-g4miPa9uUrZz4iElkaVJgDFwKJGh8aQGM7pUL4ejXl6cu7kSb30seQOVGNMP6sW8j7DW77X68hJZ+GM7UGhXeQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "stylehacks": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz", + "integrity": "sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA==", + "dev": true, + "requires": { + "browserslist": "^4.16.0", + "postcss-selector-parser": "^6.0.4" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + } + }, + "swiper": { + "version": "6.8.4", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-6.8.4.tgz", + "integrity": "sha512-O+buF9Q+sMA0H7luMS8R59hCaJKlpo8PXhQ6ZYu6Rn2v9OsFd4d1jmrv14QvxtQpKAvL/ZiovEeANI/uDGet7g==", + "dev": true, + "requires": { + "dom7": "^3.0.0", + "ssr-window": "^3.0.0" + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "terser": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.9.0.tgz", + "integrity": "sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz", + "integrity": "sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==", + "dev": true, + "requires": { + "jest-worker": "^27.0.6", + "p-limit": "^3.1.0", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "dependencies": { + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "vue": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz", + "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==", + "dev": true + }, + "vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true + }, + "vue-loader": { + "version": "15.9.8", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.8.tgz", + "integrity": "sha512-GwSkxPrihfLR69/dSV3+5CdMQ0D+jXg8Ma1S4nQXKJAznYFX14vHdc/NetQc34Dw+rBbIJyP7JOuVb9Fhprvog==", + "dev": true, + "requires": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + } + }, + "vue-style-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", + "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", + "dev": true, + "requires": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "vue-template-compiler": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz", + "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==", + "dev": true, + "requires": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "watchpack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", + "integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webpack": { + "version": "5.61.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.61.0.tgz", + "integrity": "sha512-fPdTuaYZ/GMGFm4WrPi2KRCqS1vDp773kj9S0iI5Uc//5cszsFEDgHNaX4Rj1vobUiU1dFIV3mA9k1eHeluFpw==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.2.0", + "webpack-sources": "^3.2.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "webpack-sources": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.1.tgz", + "integrity": "sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA==", + "dev": true + } + } + }, + "webpack-cli": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz", + "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.1.0", + "@webpack-cli/info": "^1.4.0", + "@webpack-cli/serve": "^1.6.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "execa": "^5.0.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + } + }, + "webpack-dev-middleware": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.2.1.tgz", + "integrity": "sha512-Kx1X+36Rn9JaZcQMrJ7qN3PMAuKmEDD9ZISjUj3Cgq4A6PtwYsC4mpaKotSRYH3iOF6HsUa8viHKS59FlyVifQ==", + "dev": true, + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^3.1.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-dev-server": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.4.0.tgz", + "integrity": "sha512-+S0XRIbsopVjPFjCO8I07FXYBWYqkFmuP56ucGMTs2hA/gV4q2M9xTmNo5Tg4o8ffRR+Nm3AsXnQXxKRyYovrA==", + "dev": true, + "requires": { + "ansi-html-community": "^0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^3.5.2", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "del": "^6.0.0", + "express": "^4.17.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "internal-ip": "^6.2.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^3.1.0", + "selfsigned": "^1.10.11", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^7.0.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^5.2.1", + "ws": "^8.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + } + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-notifier": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/webpack-notifier/-/webpack-notifier-1.14.1.tgz", + "integrity": "sha512-OVOoiOyKHS3z9pN1nLdPY2Pf/R3wiBsN0KiPc3K6ApwMBfHbyUomQc2Mr0naeKxfqXyCBPHfQuqpL9yoL0rgkA==", + "dev": true, + "requires": { + "node-notifier": "^9.0.0", + "strip-ansi": "^6.0.0" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpackbar": { + "version": "5.0.0-3", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.0-3.tgz", + "integrity": "sha512-viW6KCYjMb0NPoDrw2jAmLXU2dEOhRrtku28KmOfeE1vxbfwCYuTbTaMhnkrCZLFAFyY9Q49Z/jzYO80Dw5b8g==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.1.0", + "consola": "^2.15.0", + "figures": "^3.2.0", + "pretty-time": "^1.1.0", + "std-env": "^2.2.1", + "text-table": "^0.2.0", + "wrap-ansi": "^7.0.0" + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", + "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/phpcs.xml b/phpcs.xml index f24a49f..9af88c2 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,5 +1,6 @@ - + + Created with the PHP Coding Standard Generator. http://edorian.github.com/php-coding-standard-generator/ app/ @@ -11,68 +12,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - Created with the PHP Coding Standard Generator. http://edorian.github.com/php-coding-standard-generator/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - + + + + + + + + + + + + - + @@ -80,13 +91,39 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - */database/migrations/* + diff --git a/phpunit.xml b/phpunit.xml index 8919029..c5bec33 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,22 +1,29 @@ - - ./tests/Unit + + ./tests/Client - - ./tests/Feature/ApiV2 + + ./tests/CRM + + + ./tests/StressTest - - - ./app - - + + + ./app + ./clients/V2/src + + + + + diff --git a/routes/api.php b/routes/api.php index 09e42d2..8527a5a 100644 --- a/routes/api.php +++ b/routes/api.php @@ -12,18 +12,18 @@ use App\Http\Controllers\Api\V2\QuestionController as V2QuestionController; use App\Http\Controllers\Api\V2\StatsController as V2StatsController; /* -|-------------------------------------------------------------------------- -| API Routes -|-------------------------------------------------------------------------- -| -| Here is where you can register API routes for your application. These -| routes are loaded by the RouteServiceProvider within a group which -| is assigned the "api" middleware group. Enjoy building your API! -| + |-------------------------------------------------------------------------- + | API Routes + |-------------------------------------------------------------------------- + | + | Here is where you can register API routes for your application. These + | routes are loaded by the RouteServiceProvider within a group which + | is assigned the "api" middleware group. Enjoy building your API! + | */ Route::prefix('v1')->middleware(['auth:api', 'check-brand'])->group(function () { - // Clients + // * Clients Route::prefix('customer')->group(function () { Route::get('/{type}/{value}', [CustomerController::class, 'show']); Route::post('/create', [CustomerController::class, 'store']); @@ -35,43 +35,61 @@ Route::prefix('v1')->middleware(['auth:api', 'check-brand'])->group(function () Route::post('/{hashcode}/remove-optin', [CustomerController::class, 'removeOptin']); Route::post('/login', [CustomerController::class, 'authenticate']); }); - // Optin simple + // * Optin simple Route::post('/single-optin', [CustomerController::class, 'addSingleOptin']); - // Arcadia + // * Arcadia Route::post('/arcadia/outgoing-mail', [ArcadiaController::class, 'outgoingMail']); }); Route::prefix('v2')->name('apiV2.')->group(function () { + + // * PUBLIC Route::get('/ping', [V2ApiController::class, 'ping'])->name('ping'); - Route::post('/auth', [V2ApiController::class, 'authenticate'])->name('authenticate'); + + // * PRIVATE Route::middleware('auth:api')->group(function () { + + // * OPTINS Route::post('/optin', [V2OptinController::class, 'optin'])->name('optin'); - Route::apiResource('customers', V2CustomerController::class)->except(['index']); + + // * CUSTOMERS + Route::apiResource('customers', V2CustomerController::class, [ + 'except' => 'index', + 'parameters' => ['customers' => 'serviceCustomer'] + ]); Route::prefix('customers')->name('customers.')->group(function () { Route::post('/authenticate', [V2CustomerController::class, 'authenticate'])->name('authenticate'); - Route::prefix('{customer}')->group(function () { + Route::prefix('{serviceCustomer}')->group(function () { Route::post('/activate', [V2CustomerController::class, 'activateAccount'])->name('activate'); Route::post('/answers', [V2AnswerController::class, 'storeCustomerAnswers']) ->name('answers.store'); }); }); + + // * ARCADIA Route::prefix('arcadia')->name('arcadia.')->group(function () { Route::post('/outgoing-mail/default', [V2ArcadiaController::class, 'outgoingMailDefault']) ->name('outgoingMailDefault'); Route::post('/outgoing-mail/game', [V2ArcadiaController::class, 'outgoingMailGame']) ->name('outgoingMailGame'); }); - Route::apiResource('questions', V2QuestionController::class)->only(['index']); + + // * QUESTIONS + Route::apiResource('questions', V2QuestionController::class, [ + 'parameters' => ['questions' => 'serviceQuestion'] + ])->only('index'); Route::prefix('questions')->name('questions.')->group(function () { - Route::get('/un-answered-by/{customer}', [V2QuestionController::class, 'unAnsweredBy']) + Route::get('/un-answered-by/{serviceCustomer}', [V2QuestionController::class, 'unAnsweredBy']) ->name('unAnsweredBy'); - Route::get('/with-answers/{customer}', [V2QuestionController::class, 'indexWithResponse']) + Route::get('/with-answers/{serviceCustomer}', [V2QuestionController::class, 'indexWithResponse']) ->name('indexWithResponse'); }); + + // * STATS Route::prefix('stats')->name('stats.')->group(function () { Route::get('/brand', [V2StatsController::class, 'brandStats']) ->name('brandStats'); - Route::get('/question', [V2StatsController::class, 'questionStats']) + Route::get('/question/{serviceQuestion}', [V2StatsController::class, 'questionStats']) ->name('questionStats'); }); }); diff --git a/routes/channels.php b/routes/channels.php deleted file mode 100644 index 28b4539..0000000 --- a/routes/channels.php +++ /dev/null @@ -1,18 +0,0 @@ -id === (int)$id; -}); diff --git a/routes/console.php b/routes/console.php index e05f4c9..e216465 100644 --- a/routes/console.php +++ b/routes/console.php @@ -4,14 +4,14 @@ use Illuminate\Foundation\Inspiring; use Illuminate\Support\Facades\Artisan; /* -|-------------------------------------------------------------------------- -| Console Routes -|-------------------------------------------------------------------------- -| -| This file is where you may define all of your Closure based console -| commands. Each Closure is bound to a command instance allowing a -| simple approach to interacting with each command's IO methods. -| + |-------------------------------------------------------------------------- + | Console Routes + |-------------------------------------------------------------------------- + | + | This file is where you may define all of your Closure based console + | commands. Each Closure is bound to a command instance allowing a + | simple approach to interacting with each command's IO methods. + | */ Artisan::command('inspire', function () { diff --git a/routes/web.php b/routes/web.php index ad33767..e57128e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -11,14 +11,14 @@ use App\Http\Controllers\StatsController; use Illuminate\Support\Facades\Route; /* -|-------------------------------------------------------------------------- -| Web Routes -|-------------------------------------------------------------------------- -| -| Here is where you can register web routes for your application. These -| routes are loaded by the RouteServiceProvider within a group which -| contains the "web" middleware group. Now create something great! -| + |-------------------------------------------------------------------------- + | Web Routes + |-------------------------------------------------------------------------- + | + | Here is where you can register web routes for your application. These + | routes are loaded by the RouteServiceProvider within a group which + | contains the "web" middleware group. Now create something great! + | */ Route::get('/login', [LoginController::class, 'index'])->name('login'); @@ -29,38 +29,38 @@ Route::middleware(['auth'])->group(function () { })->name('home'); Route::post('/logout', [LoginController::class, 'logout'])->name('logout'); - // Marques + // * Marques Route::resource('brands', BrandController::class)->except([ 'delete' ]); Route::match(['get', 'post'], '/brand/manage/{brand?}', [BrandController::class, 'manage']) ->name('manage-brand'); - // Services + // * Services Route::resource('services', ServiceController::class)->except([ 'delete' ]); - Route::post('/services/{service}/token/generate', [ServiceController::class, 'generateApiToken']) + Route::post('/services/{service}/token/generate', [ServiceController::class, 'generateServicePassword']) ->name('generate-token-service'); - // Clients + // * Clients Route::get('/customers', [CustomerController::class, 'index'])->name('customers.index'); Route::get('/customers/{customer}/{brand}', [CustomerController::class, 'show'])->name('customers.show'); Route::delete('/customers/{customer}/{brand}', [CustomerController::class, 'delete'])->name('customers.delete'); Route::get('/customers/export', [CustomerController::class, 'export'])->name('customers.export'); Route::post('/customers/export', [CustomerController::class, 'exportSubmit'])->name('customers.exportSubmit'); - // Single optins + // * Single optins Route::resource('single_optins', SingleOptinController::class); Route::post('/single_optins/{id}/delete', [SingleOptinController::class, 'delete'])->name('single-optins-delete'); - // Questions + // * Questions Route::get('/questions/export', [QuestionController::class, 'export'])->name('questions.export'); Route::post('/questions/export', [QuestionController::class, 'exportSubmit'])->name('questions.exportSubmit'); Route::resource('questions', QuestionController::class); Route::resource('questions.answers', AnswerController::class); - // Stats + // * Stats Route::match(['get', 'post'], '/stats/brand', [StatsController::class, 'viewStatsByBrand']) ->name('stats.viewByBrand'); Route::get('/stats/brand/csv', [StatsController::class, 'exportByBrandCsv']) diff --git a/test.sh b/test.sh index 12a786f..4024d17 100755 --- a/test.sh +++ b/test.sh @@ -1,2 +1,13 @@ #!/bin/sh -composer runtestmigration > /dev/null; composer runserve; composer runtest; composer killserve; + +composer runtestmigration >/dev/null +composer runserve +# If argument is passed run with coverage +if [ -z "$1" ]; then + echo 'no coverage' + composer runtest +else + echo 'with coverage' + composer runtestWithCoverage +fi +composer killserve diff --git a/tests/Feature/ApiV2/ApiBase.php b/tests/CRM/ApiV2/ApiBase.php similarity index 58% rename from tests/Feature/ApiV2/ApiBase.php rename to tests/CRM/ApiV2/ApiBase.php index 0303a08..e3e5054 100644 --- a/tests/Feature/ApiV2/ApiBase.php +++ b/tests/CRM/ApiV2/ApiBase.php @@ -1,9 +1,11 @@ assertFragment(['status' => $status->value]); @@ -26,27 +35,26 @@ class ApiBase extends BaseTestCase * * @param string $method * @param string $uri - * @param array $data - * @param array $headers + * @param array $data + * @param array $headers * @return \Illuminate\Testing\TestResponse */ protected function callApi(string $method, string $uri, array $data = [], array $headers = []): TestResponse { - $time = \time(); - $headers['X-API-TIMESTAMP'] = $time; - $data['timestamp'] = $time; + if (!isset($data['timestamp'])) { + $data['timestamp'] = \time(); + } return $this->json($method, $uri, $data, $headers); } /** * Raw Function to call api with Token * - * @param Customer $customer * @param Service $service - * @param string $method - * @param string $uri - * @param array $data - * @param array $headers + * @param string $method + * @param string $uri + * @param array $data + * @param array $headers * @return \Illuminate\Testing\TestResponse */ protected function callApiWithToken( @@ -56,44 +64,41 @@ class ApiBase extends BaseTestCase array $data = [], array $headers = [] ): TestResponse { - $apiToken = $service->api_token; - $apiToken = $service->generateApiToken(); - $service->saveOrFail(); - $data['api_token'] = $apiToken; + $data['bearer_token'] = $service->bearer_token; + $data['timestamp'] = \gmdate('Y-m-d H:i:s'); + + $data['signature'] = Helper::signRequest( + $service->bearer_pwd, + $uri, + $method, + $data + ); + return $this->callApi($method, $uri, $data, $headers); } /** * Get Customer models with given status (active or not) * - * @param boolean $status the customer status for a brand + * @param boolean $status The customer status for a brand. + * @param boolean $random Randomize the customer to get. * @return array */ protected function getCustomer(bool $status = true, bool $random = true): array { - /** @var \App\Models\Costumer $customer */ - $customer = Customer::whereHas('logins', function ($q) use ($status) { - $q->where('status', $status); - }); + $login = CustomerLogin::whereStatus($status); if ($random) { - $customer = $customer->inRandomOrder(); + $login = $login->inRandomOrder(); } - $customer = $customer->firstOrFail(); - /** @var \App\Models\Login $login */ - $login = $customer->logins()->where('status', $status)->firstOrFail(); + /** @var \App\Models\CustomerLogin $login */ + $login = $login->firstOrFail(); /** @var \App\Models\Brand $brand */ $brand = $login->brand; + /** @var \App\Models\Customer $customer */ + $customer = $login->customer; /** @var \App\Models\Service $service */ $service = $brand->services()->firstOrFail(); - - // regen bearer token to get the pwd in clear - $bearer_token = $service->generateBearerToken(); - $bearer_pwd = $service->bearer_pwd; - $service->saveOrFail(); - $service->bearer_token = $bearer_token; - $service->bearer_pwd = $bearer_pwd; - - // get customer info + /** @var \App\Models\CustomerInfo $info */ $info = $customer->info($brand, Customer::FIRST_OR_FAIL); return compact('customer', 'service', 'login', 'brand', 'info'); diff --git a/tests/CRM/ApiV2/ApiTest.php b/tests/CRM/ApiV2/ApiTest.php new file mode 100644 index 0000000..3dfe850 --- /dev/null +++ b/tests/CRM/ApiV2/ApiTest.php @@ -0,0 +1,186 @@ +call('get', route('apiV2.ping'), [ + 'timestamp' => \gmdate('Y-m-d H:i:s') + ]); + $response->assertStatus(400); + } + + /** + * Test timestamp middleware works as expected. + * + * @return void + */ + public function testTimestampMiddleware() + { + $response = $this->json('get', route('apiV2.ping'), [ + 'timestamp' => \gmdate('Y-m-d H:i:s') + ]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + } + + /** + * Timestamp middleware should fail if timestamp parameter is missing. + * + * @return void + */ + public function testTimestampMiddlewareCheckFailMissing() + { + $response = $this->json('get', route('apiV2.ping'), []); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $json->assertFragment(['data' => [ + 'code' => ApiError::wrongTimeStamp()->value, + 'message' => ApiError::wrongTimeStamp()->label + ]]); + } + + /** + * Timestamp middleware should fail if timestamp parameter has wrong format. + * + * @return void + */ + public function testTimestampMiddlewareCheckFailWrongFormat() + { + $response = $this->json('get', route('apiV2.ping'), ['timestamp' => \gmdate('Y/m/d')]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $json->assertFragment(['data' => [ + 'code' => ApiError::wrongTimeStamp()->value, + 'message' => ApiError::wrongTimeStamp()->label + ]]); + } + + /** + * Timestamp middleware should fail if timestamp is older than "config('api.timestampTimeout', 10)". + * + * @return void + */ + public function testTimestampMiddlewareCheckFailIfTooOld() + { + $response = $this->json('get', route('apiV2.ping'), [ + 'timestamp' => \gmdate('Y-m-d H:i:s', \time() - config('api.timestampTimeout', 10)) + ]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $json->assertFragment(['data' => [ + 'code' => ApiError::wrongTimeStamp()->value, + 'message' => ApiError::wrongTimeStamp()->label + ]]); + } + + /** + * Test api route throttle. + * + * ! This has to be the last test of this class. + * + * @return void + */ + public function testApiThrottle() + { + for ($i = config('api.apiRequestRatePerMinute', 100); $i >= 0; $i--) { + $this->json('get', route('apiV2.ping'), [ + 'timestamp' => \gmdate('Y-m-d H:i:s') + ]); + } + + $response = $this->json('get', route('apiV2.ping'), [ + 'timestamp' => \gmdate('Y-m-d H:i:s') + ]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $response->assertStatus(429); + } + + /** + * Test wrong http method call + * + * @return void + */ + public function testWrongMethodCall() + { + $response = $this->json('post', route('apiV2.ping'), [ + 'timestamp' => \gmdate('Y-m-d H:i:s', \time()) + ]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::methodNotAllowed()->value, $json->json('data.code')); + } + + /** + * Test Auth fail when no token + * + * @return void + */ + public function testAuthFailNoToken() + { + $response = $this->json('get', route('apiV2.customers.show', [ + 'serviceCustomer' => Str::uuid() + ]), [ + 'timestamp' => \gmdate('Y-m-d H:i:s', \time()) + ]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::apiAuthFailed()->value, $json->json('data.code')); + } + + /** + * Test Auth fail when unknown token + * + * @return void + */ + public function testAuthFailUnknownToken() + { + $response = $this->json('get', route('apiV2.customers.show', [ + 'serviceCustomer' => Str::uuid() + ]), [ + 'bearer_token' => 'qsdqsdqsd', + 'timestamp' => \gmdate('Y-m-d H:i:s', \time()) + ]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::apiAuthFailed()->value, $json->json('data.code')); + } + + /** + * Test Auth fail when wrong signature + * + * @return void + */ + public function testAuthFailWrongSignature() + { + $service = Service::query()->firstOrFail(); + $response = $this->json('get', route('apiV2.customers.show', [ + 'serviceCustomer' => Str::uuid() + ]), [ + 'bearer_token' => $service->bearer_token, + 'timestamp' => \gmdate('Y-m-d H:i:s', \time()), + 'signature' => 'sdqdqsdqd' + ]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::apiAuthFailed()->value, $json->json('data.code')); + } +} diff --git a/tests/Feature/ApiV2/ArcadiaTest.php b/tests/CRM/ApiV2/ArcadiaTest.php similarity index 92% rename from tests/Feature/ApiV2/ArcadiaTest.php rename to tests/CRM/ApiV2/ArcadiaTest.php index 73a7616..c0f080a 100644 --- a/tests/Feature/ApiV2/ArcadiaTest.php +++ b/tests/CRM/ApiV2/ArcadiaTest.php @@ -1,6 +1,6 @@ assertApiResponseStatusIs($json, ApiStatus::OK()); } + /** + * Test arcadia send game reclamation. + * apiV2.arcadia.outgoingMailGame + * + * @return void + */ public function testArcadiaSendOrderGame() { extract($this->getCustomer()); diff --git a/tests/CRM/ApiV2/CustomerTest.php b/tests/CRM/ApiV2/CustomerTest.php new file mode 100644 index 0000000..ed3bc2b --- /dev/null +++ b/tests/CRM/ApiV2/CustomerTest.php @@ -0,0 +1,710 @@ +getCustomer()); + $infos = $customer->infos; + $logins = $customer->logins; + $brands = $customer->brands; + $optInHistory = $customer->optInHistory; + $this->assertInstanceOf(Collection::class, $infos); + $this->assertInstanceOf(Collection::class, $logins); + $this->assertInstanceOf(Collection::class, $brands); + $this->assertInstanceOf(Collection::class, $optInHistory); + $this->assertInstanceOf(CustomerInfo::class, $customer->info($brands->first())); + $this->assertInstanceOf(CustomerLogin::class, $customer->login($brands->first())); + } + + /** + * Test customer Authentification ok. + * route: apiV2.customers.authenticate + * + * @return void + */ + public function testApiAuthenticateCustomerShouldWork() + { + extract($this->getCustomer(true)); + + $login->password = 'testPassword'; + $login->saveOrFail(); + + $response = $this->callApiWithToken( + $service, + 'post', + route('apiV2.customers.authenticate', []), + [ + 'email' => $customer->email, + 'password' => 'testPassword' + ] + ); + + $json = $response->decodeResponseJson(); + + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + $this->assertNotEmpty($json->json('data')); + $this->assertNotEmpty($json->json('data.firstname')); + $this->assertNotEmpty($json->json('data.lastname')); + $this->assertNotEmpty($json->json('data.birth_date')); + $this->assertNotEmpty($json->json('data.address')); + $this->assertNotEmpty($json->json('data.phone')); + $this->assertNotEmpty($json->json('data.country')); + $this->assertNotEmpty($json->json('data.city')); + $this->assertNotEmpty($json->json('data.postcode')); + $this->assertNotEmpty($json->json('data.hashcode')); + } + + /** + * Customer authentification should fail if the password is wrong. + * route: apiV2.customers.authenticate + * + * @return void + */ + public function testApiAuthenticateCustomerShouldFailIfWrongPassword() + { + extract($this->getCustomer()); + $response = $this->callApiWithToken( + $service, + 'post', + route('apiV2.customers.authenticate', []), + [ + 'email' => $customer->email, + 'password' => 'azdqdqdazd' + ] + ); + + $json = $response->decodeResponseJson(); + + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertNotEmpty($json->json('data')); + $this->assertNotEmpty($json->json('data.message')); + $this->assertNotEmpty($json->json('data.code')); + $this->assertEquals(ApiError::authFailed()->value, $json->json('data.code')); + $this->assertEquals(ApiError::authFailed()->label, $json->json('data.message')); + } + + /** + * Customer authentification should fail if the email is wrong. + * route: apiV2.customers.authenticate + * + * @return void + */ + public function testApiAuthenticateCustomerShouldFailIfWrongEmail() + { + extract($this->getCustomer()); + $response = $this->callApiWithToken( + $service, + 'post', + route('apiV2.customers.authenticate', []), + [ + 'email' => 'wrongemail@hotmail.fr', + 'password' => 'azdqdqdazd' + ] + ); + + $json = $response->decodeResponseJson(); + + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertNotEmpty($json->json('data')); + $this->assertNotEmpty($json->json('data.message')); + $this->assertNotEmpty($json->json('data.code')); + $this->assertEquals(ApiError::authFailed()->value, $json->json('data.code')); + $this->assertEquals(ApiError::authFailed()->label, $json->json('data.message')); + } + + /** + * Customer authentification should fail if the customer is not activated. + * route: apiV2.customers.authenticate + * + * @return void + */ + public function testApiAuthenticateCustomerShouldFailIfCustomerNotActivated() + { + extract($this->getCustomer(false)); + $response = $this->callApiWithToken( + $service, + 'post', + route('apiV2.customers.authenticate', []), + [ + 'email' => $customer->email, + 'password' => 'testPassword' + ] + ); + + $json = $response->decodeResponseJson(); + + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertNotEmpty($json->json('data')); + $this->assertNotEmpty($json->json('data.message')); + $this->assertNotEmpty($json->json('data.code')); + $this->assertEquals(ApiError::customerNotActivated()->value, $json->json('data.code')); + $this->assertEquals(ApiError::customerNotActivated()->label, $json->json('data.message')); + } + + /** + * Customer store ok + * route: apiV2.customers.store + * + * @return void + */ + public function testApiStoreCustomerOk() + { + extract($this->getCustomer()); + + // * Single optin should be deleted when creating new customer. + $singleOptin = new SingleOptin(); + $singleOptin->email = 'newcustomer@hotmail.fr'; + $singleOptin->brand()->associate($brand); + $singleOptin->saveOrFail(); + + $data = [ + 'firstname' => 'Jean', + 'lastname' => 'Bond', + 'birth_date' => \date('1992-03-08'), + 'address' => '10 rue des toto', + 'email' => 'newcustomer@hotmail.fr', + 'password' => '123.Mot.De.P4sse', + 'phone' => '+33654821562', + 'country' => Country::france()->value, + 'city' => 'Connerré', + 'postcode' => '72160', + 'opt_in' => true + ]; + $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $data); + $json = $response->decodeResponseJson(); + + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + $this->assertNotEmpty($json->json('data.firstname')); + $this->assertNotEmpty($json->json('data.lastname')); + $this->assertNotEmpty($json->json('data.birth_date')); + $this->assertNotEmpty($json->json('data.address')); + $this->assertNotEmpty($json->json('data.phone')); + $this->assertNotEmpty($json->json('data.country')); + $this->assertNotEmpty($json->json('data.city')); + $this->assertNotEmpty($json->json('data.postcode')); + $this->assertNotEmpty($json->json('data.hashcode')); + + $this->assertEquals($data['firstname'], $json->json('data.firstname')); + $this->assertEquals($data['lastname'], $json->json('data.lastname')); + $this->assertEquals($data['birth_date'], $json->json('data.birth_date')); + $this->assertEquals($data['address'], $json->json('data.address')); + $this->assertEquals($data['phone'], $json->json('data.phone')); + $this->assertEquals($data['country'], $json->json('data.country')); + $this->assertEquals($data['city'], $json->json('data.city')); + $this->assertEquals($data['postcode'], $json->json('data.postcode')); + } + + /** + * Customer store should fail if customer already exists. + * route: apiV2.customers.store + * + * @return void + */ + public function testApiStoreCustomerShouldFailIfCustomerAlreadyExists() + { + extract($this->getCustomer()); + + $data = [ + 'firstname' => 'Jean', + 'lastname' => 'Bond', + 'birth_date' => \date('1992-03-08'), + 'address' => '10 rue des toto', + 'email' => 'newcustomer@hotmail.fr', + 'password' => '123.Mot.De.P4sse', + 'phone' => '+33654821562', + 'country' => Country::france()->value, + 'city' => 'Connerré', + 'postcode' => '72160', + 'opt_in' => true + ]; + + $this->createNewCustomer($data, $brand); + + $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $data); + $json = $response->decodeResponseJson(); + + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::customerExists()->value, $json->json('data.code')); + $this->assertEquals(ApiError::customerExists()->label, $json->json('data.message')); + } + + /** + * Customer store validationFailures + * route: apiV2.customers.store + * + * @return void + */ + public function testApiStoreCustomerValidationFailures() + { + extract($this->getCustomer()); + $data = [ + 'firstname' => 'Jean', + 'lastname' => 'Bond', + 'birth_date' => \date('1992-03-08'), + 'address' => '10 rue des toto', + 'email' => 'newcustomer.failures@hotmail.fr', + 'password' => '123.Mot.De.P4sse', + 'phone' => '+33654821562', + 'country' => Country::france()->value, + 'city' => 'Connerré', + 'postcode' => '72160', + 'opt_in' => true + ]; + + $sendData = $data; + $sendData['password'] = 'password'; + + $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $sendData); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals( + ApiError::validationFailed()->value, + $json->json('data.code'), + 'Password strengh should be checked' + ); + + $sendData = $data; + $sendData['postcode'] = '12'; + + $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $sendData); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals( + ApiError::validationFailed()->value, + $json->json('data.code'), + 'Post code should be checked' + ); + + $sendData = $data; + $sendData['country'] = '12'; + + $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $sendData); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals( + ApiError::validationFailed()->value, + $json->json('data.code'), + 'Country code should be checked' + ); + + $sendData = $data; + $sendData['phone'] = '+49-89-636-48018'; + + $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $sendData); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals( + ApiError::validationFailed()->value, + $json->json('data.code'), + 'Country code should be checked' + ); + + $sendData = $data; + $sendData['email'] = 'toto@nonexistingfqdndomain.lol'; + + $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $sendData); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals( + ApiError::validationFailed()->value, + $json->json('data.code'), + 'Email code should be checked' + ); + } + + /** + * Test activate customer ok + * route: apiV2.customers.activate + * + * @return void + */ + public function testActivateCustomerOk() + { + extract($this->getCustomer(false)); + + $login->status = false; + $login->saveOrFail(); + + $response = $this->callApiWithToken($service, 'post', route( + 'apiV2.customers.activate', + ['serviceCustomer' => $info->hashcode] + ), []); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + } + + /** + * Test activate customer already activated + * route: apiV2.customers.activate + * + * @return void + */ + public function testActivateCustomerAlreadyActivated() + { + extract($this->getCustomer(true)); + + $login->status = true; + $login->saveOrFail(); + + $response = $this->callApiWithToken($service, 'post', route( + 'apiV2.customers.activate', + ['serviceCustomer' => $info->hashcode] + ), []); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::customerAlreadyActive()->value, $json->json('data.code')); + $this->assertEquals(ApiError::customerAlreadyActive()->label, $json->json('data.message')); + } + + /** + * Test customer update ok + * route: apiV2.customers.update + * + * @return void + */ + public function testApiUpdateCustomerOk() + { + extract($this->getCustomer()); + $data = [ + 'firstname' => 'Jean2', + 'lastname' => 'Bond2', + 'birth_date' => \date('1992-03-09'), + 'address' => '10 rue des toto', + 'email' => 'newcustomertoupdate@hotmail.fr', + 'password' => '123.Mot.De.P4sseNew', + 'phone' => '+33654821563', + 'country' => Country::luxembourg()->value, + 'city' => 'Connerré2', + 'postcode' => '72161', + 'opt_in' => 0 + ]; + + $newdata = [ + 'firstname' => 'Jean3', + 'lastname' => 'Bond3', + 'birth_date' => \date('1992-03-07'), + 'address' => '10 rue des toto3', + 'email' => 'newcustomertoupdate3@hotmail.fr', + 'password' => '123.Mot.De.P4sseNew3', + 'phone' => '+33654821564', + 'country' => Country::france()->value, + 'city' => 'Connerré3', + 'postcode' => '72163', + 'opt_in' => 1 + ]; + + $this->createNewCustomer($data, $brand); + + foreach ($data as $rowName => $row) { + $response = $this->callApiWithToken($service, 'patch', route( + 'apiV2.customers.update', + ['serviceCustomer' => $info->hashcode] + ), [$rowName => $newdata[$rowName]]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + if ($rowName != 'password') { + $this->assertNotNull($json->json('data.' . $rowName), "Failed asserting $rowName is not null"); + $this->assertEquals( + $newdata[$rowName], + $json->json('data.' . $rowName), + "Failed asserting $rowName is equal to $newdata[$rowName]" + ); + } else { + $this->assertNull($json->json('data.' . $rowName), "Failed asserting $rowName is null"); + $this->assertEquals( + null, + $json->json('data.' . $rowName), + "Failed asserting $rowName is equal to null" + ); + } + }//end foreach + + // Address2 does not exists in crm. + $response = $this->callApiWithToken($service, 'patch', route( + 'apiV2.customers.update', + ['serviceCustomer' => $info->hashcode] + ), ['address2' => 'anything']); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::validationFailed()->value, $json->json('data.code')); + } + + /** + * Test customer update should fail if no changes are provided + * route: apiV2.customers.update + * + * @return void + */ + public function testApiUpdateCustomerShouldFailWhenNothingChanges() + { + extract($this->getCustomer()); + $data = [ + 'firstname' => 'Jean2', + 'lastname' => 'Bond2', + 'birth_date' => \date('1992-03-09'), + 'address' => '10 rue des toto', + 'email' => 'anewcustomertoupdate@hotmail.fr', + 'password' => '123.Mot.De.P4sseNew', + 'phone' => '+33654821563', + 'country' => Country::luxembourg()->value, + 'city' => 'Connerré2', + 'postcode' => '72161', + 'opt_in' => 0 + ]; + + $this->createNewCustomer($data, $brand); + + $response = $this->callApiWithToken($service, 'patch', route( + 'apiV2.customers.update', + ['serviceCustomer' => $info->hashcode] + )); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals($json->json('data.code'), ApiError::validationFailed()->value); + } + + /** + * Test customer show ok + * route: apiV2.customers.show + * + * @return void + */ + public function testApiGetCustomerOk() + { + extract($this->getCustomer()); + + $response = $this->callApiWithToken($service, 'get', route( + 'apiV2.customers.show', + ['serviceCustomer' => $info->hashcode] + )); + $json = $response->decodeResponseJson(); + + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + $this->assertNotEmpty($json->json('data.firstname')); + $this->assertNotEmpty($json->json('data.lastname')); + $this->assertNotEmpty($json->json('data.birth_date')); + $this->assertNotEmpty($json->json('data.address')); + $this->assertNotEmpty($json->json('data.phone')); + $this->assertNotEmpty($json->json('data.country')); + $this->assertNotEmpty($json->json('data.city')); + $this->assertNotEmpty($json->json('data.postcode')); + $this->assertNotEmpty($json->json('data.hashcode')); + } + + /** + * Test customer show using email ok + * route: apiV2.customers.show + * + * @return void + */ + public function testApiGetCustomerByEmailOk() + { + extract($this->getCustomer()); + + $response = $this->callApiWithToken($service, 'get', route( + 'apiV2.customers.show', + ['serviceCustomer' => $customer->email] + )); + $json = $response->decodeResponseJson(); + + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + $this->assertNotEmpty($json->json('data.firstname')); + $this->assertNotEmpty($json->json('data.lastname')); + $this->assertNotEmpty($json->json('data.birth_date')); + $this->assertNotEmpty($json->json('data.address')); + $this->assertNotEmpty($json->json('data.phone')); + $this->assertNotEmpty($json->json('data.country')); + $this->assertNotEmpty($json->json('data.city')); + $this->assertNotEmpty($json->json('data.postcode')); + $this->assertNotEmpty($json->json('data.hashcode')); + } + + /** + * Test customer show not found + * route: apiV2.customers.show + * + * @return void + */ + public function testApiGetCustomerNotFound() + { + extract($this->getCustomer()); + $csClassName = \array_slice(explode('\\', get_class($customer)), -1)[0]; + + $uuid = Str::uuid(); + + $response = $this->callApiWithToken($service, 'get', route( + 'apiV2.customers.show', + ['serviceCustomer' => $uuid] + )); + $json = $response->decodeResponseJson(); + + /** @var \App\Models\Customer */ + $customer = Customer::whereHas('infos', function ($q) use ($uuid) { + $q->where('hashcode', $uuid); + })->first(); + + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::modelNotFound()->value, $json->json('data.code')); + $this->assertEquals( + sprintf(ApiError::modelNotFound()->label, $csClassName), + $json->json('data.message') + ); + } + + /** + * Test delete customer. + * route: apiV2.customer.destroy + * + * @return void + */ + public function testApiDestroyCustomer() + { + extract($this->getCustomer()); + $csClassName = \array_slice(explode('\\', get_class($customer)), -1)[0]; + + $service = $brand->services()->firstOrFail(); + $response = $this->callApiWithToken($service, 'delete', route( + 'apiV2.customers.destroy', + ['serviceCustomer' => $info->hashcode] + )); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + $this->assertEmpty($json->json('data')); + + $response = $this->callApiWithToken($service, 'get', route( + 'apiV2.customers.show', + ['serviceCustomer' => $info->hashcode] + )); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals( + sprintf(ApiError::modelNotFound()->label, $csClassName), + $json->json('data.message'), + 'Customer should have been deleted.' + ); + } + + /** + * Test delete customer all of data. + * route: apiV2.customer.destroy + * + * @return void + */ + public function testApiCompletelyDestroyCustomer() + { + $customer = Customer::whereHas('logins', null, '>', 1)->firstOrFail(); + + /** @var \Illuminate\Database\Eloquent\Collection $logins */ + $logins = $customer->logins; + /** @var \App\Models\CustomerLogin $keepLogin */ + $keepLogin = $logins->pull(0); + + $logins->map(function (CustomerLogin $login) use ($customer) { + CustomerInfo::whereHas('customer', function ($q) use ($customer) { + $q->whereId($customer->id); + })->whereHas('brand', function ($q) use ($login) { + $q->whereId($login->brand->id); + })->delete(); + $login->delete(); + }); + + $this->assertTrue( + (bool)$customer->logins()->count(), + 'This test assumes customer belongs to multiple brand.' + ); + + $csClassName = \array_slice(explode('\\', get_class($customer)), -1)[0]; + + /** @var \App\Models\CustomerInfo $info */ + $info = $customer->info($keepLogin->brand, Customer::FIRST_OR_FAIL); + /** @var \App\Models\Service $service */ + $service = $keepLogin->brand->services()->firstOrFail(); + + $response = $this->callApiWithToken($service, 'delete', route( + 'apiV2.customers.destroy', + ['serviceCustomer' => $info->hashcode] + )); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + $this->assertEmpty($json->json('data')); + + $response = $this->callApiWithToken($service, 'get', route( + 'apiV2.customers.show', + ['serviceCustomer' => $info->hashcode] + )); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals( + sprintf(ApiError::modelNotFound()->label, $csClassName), + $json->json('data.message'), + 'Customer should have been deleted.' + ); + } + + /** + * Make sute a customer exists and contains data + * + * @param array $data + * @param Brand $brand + * @return void + */ + private function createNewCustomer(array $data, Brand $brand) + { + $customer = Customer::whereEmail($data['email']) + ->whereHas('brands', function ($q) use ($brand) { + $q->where('brands.id', '=', $brand->id); + })->firstOrNew(); + $customer->fill($data); + $saved = $customer->save(); + + // * Delete Single optin if exists. + /** @var \App\Models\SingleOptin|null $singleOptin */ + $singleOptin = SingleOptin::where('email', $data['email']) + ->whereHas('brand', function ($q) use ($brand) { + $q->where('brands.id', '=', $brand->id); + })->first(); + if ($singleOptin) { + $saved = $singleOptin->delete() && $saved; + } + + /** @var \App\Models\CustomerInfo */ + $customerInfo = $customer->info($brand, Customer::FIRST_OR_NEW); + $customerInfo->fill($data); + $customerInfo->brand()->associate($brand); + $customerInfo->customer()->associate($customer); + $saved = $customerInfo->save() && $saved; + + /** @var \App\Models\CustomerLogin */ + $customerLogin = $customer->login($brand, Customer::FIRST_OR_NEW); + $customerLogin->fill($data); + $customerLogin->brand()->associate($brand); + $customerLogin->customer()->associate($customer); + $saved = $customerLogin->save() && $saved; + + $this->assertTrue($saved, 'A new customer should have been created.'); + } +} diff --git a/tests/CRM/ApiV2/QuestionTest.php b/tests/CRM/ApiV2/QuestionTest.php new file mode 100644 index 0000000..f7fe868 --- /dev/null +++ b/tests/CRM/ApiV2/QuestionTest.php @@ -0,0 +1,303 @@ +getCustomer()); + + /** @var \Illuminate\Database\Eloquent\Collection $questions */ + $questions = Question::whereHas('brand', function ($q) use ($service) { + $q->whereId($service->brand->id); + })->with('answers')->get()->keyBy('id'); + + $response = $this->callApiWithToken($service, 'get', route('apiV2.questions.index')); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + + $json->assertStructure([ + 'data' => [ + '*' => [ + 'id', + 'type', + 'question', + 'answers' => [ + '*' => [ + 'id', + 'answer' + ] + ], + ] + ] + ]); + + // Should contain questions with answers. + foreach ($json->json('data') as $question) { + $this->assertTrue($questions->contains($question['id'])); + $DBquestion = $questions->get($question['id']); + $this->assertEquals($DBquestion->type->value, $question['type']); + $this->assertEquals($DBquestion->question, $question['question']); + foreach ($question['answers'] as $answer) { + $answers = $DBquestion->answers->keyBy('id'); + $this->assertTrue($answers->contains($answer['id'])); + $DBanswer = $answers->get($answer['id']); + $this->assertEquals($DBanswer->answer, $answer['answer']); + } + } + } + + /** + * Test get all service questions with customer answers. + * route: apiV2.questions.indexWithResponse + * @return void + */ + public function testQuestionIndexWithAnswers() + { + extract($this->getCustomer(true, false)); + + /** @var \Illuminate\Database\Eloquent\Collection $questions */ + $questions = Question::whereHas('brand', function ($q) use ($service) { + $q->whereId($service->brand->id); + })->with('answers')->get()->keyBy('id'); + + + // * Make sure a customer answer is set. + $firstAnswer = $questions->first()->answers->first(); + $firstAnswer->load('question'); + + /** @var \App\Models\CustomerAnswer $custAnswer */ + $custAnswer = CustomerAnswer::whereHas('customer', function ($q) use ($customer) { + $q->whereId($customer->id); + })->whereHas('answer', function ($q) use ($firstAnswer) { + $q->whereId($firstAnswer->id); + })->firstOrNew(); + + $custAnswer->customer()->associate($customer); + $custAnswer->answer()->associate($firstAnswer); + $custAnswer->saveOrFail(); + + // * Call + $response = $this->callApiWithToken($service, 'get', route( + 'apiV2.questions.indexWithResponse', + ['serviceCustomer' => $info->hashcode] + )); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + + $json->assertStructure([ + 'data' => [ + '*' => [ + 'id', + 'type', + 'question', + 'answers' => [ + '*' => [ + 'id', + 'answer' + ] + ], + 'customerAnswers' + ] + ] + ]); + + // Should contain questions with answers. + foreach ($json->json('data') as $question) { + $this->assertTrue($questions->contains($question['id'])); + $DBquestion = $questions->get($question['id']); + $this->assertEquals($DBquestion->type->value, $question['type']); + $this->assertEquals($DBquestion->question, $question['question']); + foreach ($question['answers'] as $answer) { + $answers = $DBquestion->answers->keyBy('id'); + $this->assertTrue($answers->contains($answer['id'])); + $DBanswer = $answers->get($answer['id']); + $this->assertEquals($DBanswer->answer, $answer['answer']); + } + if ($firstAnswer->question->id === $DBquestion->id) { + $this->assertEquals($question['customerAnswers'][0]['id'], $firstAnswer->id); + $this->assertEquals($question['customerAnswers'][0]['answer'], $firstAnswer->answer); + } + } + } + + /** + * Test get all service questions with customer answers when customer is unknown. + * route: apiV2.questions.indexWithResponse + * @return void + */ + public function testQuestionIndexWithAnswersWhenCustomerNotFound() + { + extract($this->getCustomer(true, false)); + $service = Service::whereHas('brand', function ($q) use ($brand) { + $q->where('id', '!=', $brand->id); + })->firstOrFail(); + $response = $this->callApiWithToken($service, 'get', route( + 'apiV2.questions.indexWithResponse', + ['serviceCustomer' => $info->hashcode] + )); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::modelNotFound()->value, $json->json('data.code')); + } + + /** + * Test get all service questions with customer answers when customer is unknown. + * route: apiV2.questions.unAnsweredBy + * @return void + */ + public function testCustomerUnAnsweredIndex() + { + extract($this->getCustomer(true, false)); + + /** @var \Illuminate\Database\Eloquent\Collection $questions */ + $questions = Question::whereHas('brand', function ($q) use ($service) { + $q->whereId($service->brand->id); + })->with('answers')->get()->keyBy('id'); + + + // * Make sure a customer answer is set. + $firstAnswer = $questions->first()->answers->first(); + $firstAnswer->load('question'); + + /** @var \App\Models\CustomerAnswer $custAnswer */ + $custAnswer = CustomerAnswer::whereHas('customer', function ($q) use ($customer) { + $q->whereId($customer->id); + })->whereHas('answer', function ($q) use ($firstAnswer) { + $q->whereId($firstAnswer->id); + })->firstOrNew(); + + $custAnswer->customer()->associate($customer); + $custAnswer->answer()->associate($firstAnswer); + $custAnswer->saveOrFail(); + + // * Call + $response = $this->callApiWithToken($service, 'get', route( + 'apiV2.questions.unAnsweredBy', + ['serviceCustomer' => $info->hashcode] + )); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + + $json->assertStructure([ + 'data' => [ + '*' => [ + 'id', + 'type', + 'question', + 'answers' => [ + '*' => [ + 'id', + 'answer' + ] + ] + ] + ] + ]); + + // Should contain questions with answers. + foreach ($json->json('data') as $question) { + $this->assertTrue($questions->contains($question['id'])); + $DBquestion = $questions->get($question['id']); + $this->assertEquals($DBquestion->type->value, $question['type']); + $this->assertEquals($DBquestion->question, $question['question']); + foreach ($question['answers'] as $answer) { + $answers = $DBquestion->answers->keyBy('id'); + $this->assertTrue($answers->contains($answer['id'])); + $DBanswer = $answers->get($answer['id']); + $this->assertEquals($DBanswer->answer, $answer['answer']); + } + $this->assertNotEquals( + $firstAnswer->question->id, + $DBquestion->id, + 'This question has been answered by our user' + ); + } + } + + /** + * Test get all service questions unanswered by customer when customer is unknown. + * route: apiV2.questions.indexWithResponse + * @return void + */ + public function testUnAnsweredIndexWhenCustomerNotFound() + { + extract($this->getCustomer(true, false)); + $service = Service::whereHas('brand', function ($q) use ($brand) { + $q->where('id', '!=', $brand->id); + })->firstOrFail(); + $response = $this->callApiWithToken($service, 'get', route( + 'apiV2.questions.unAnsweredBy', + ['serviceCustomer' => $info->hashcode] + )); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::modelNotFound()->value, $json->json('data.code')); + } + + /** + * Test save customer answer. + * route: apiV2.customers.answers.store + * @return void + */ + public function testQuestionSetAnswer() + { + extract($this->getCustomer(true, false)); + $question = Question::whereHas('brand', function ($q) use ($brand) { + $q->whereId($brand->id); + })->whereHas('answers', function ($q) use ($customer) { + $q->whereDoesntHave('customers', function ($q) use ($customer) { + $q->whereId($customer->id); + }); + })->inRandomOrder()->firstOrFail(); + + $answer = $question->answers->first(); + $response = $this->callApiWithToken($service, 'post', route( + 'apiV2.customers.answers.store', + ['serviceCustomer' => $info->hashcode] + ), [ + 'question' => $question->id, + 'answers' => [$answer->id] + ]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + + $this->assertTrue(CustomerAnswer::whereHas('customer', function ($q) use ($customer) { + $q->whereId($customer->id); + })->whereHas('answer', function ($q) use ($answer) { + $q->whereId($answer->id); + })->exists(), 'Question should have been answered.'); + } + + /** + * Test get all save customer answers when customer is unknown. + * route: apiV2.questions.indexWithResponse + * @return void + */ + public function testQuestionSetAnswerWhenCustomerNotFound() + { + extract($this->getCustomer(true, false)); + $service = Service::whereHas('brand', function ($q) use ($brand) { + $q->where('id', '!=', $brand->id); + })->firstOrFail(); + $response = $this->callApiWithToken($service, 'post', route( + 'apiV2.customers.answers.store', + ['serviceCustomer' => $info->hashcode] + )); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::modelNotFound()->value, $json->json('data.code')); + } +} diff --git a/tests/Feature/ApiV2/SingleOptinTest.php b/tests/CRM/ApiV2/SingleOptinTest.php similarity index 54% rename from tests/Feature/ApiV2/SingleOptinTest.php rename to tests/CRM/ApiV2/SingleOptinTest.php index 2b8a035..fac6844 100644 --- a/tests/Feature/ApiV2/SingleOptinTest.php +++ b/tests/CRM/ApiV2/SingleOptinTest.php @@ -1,6 +1,6 @@ callApiWithToken($service, 'post', route('apiV2.optin'), [ - 'email' => 'warthogdj@hotmail.fr', + 'email' => 'singleoptin@hotmail.fr', 'opt_in' => 1 ]); $json = $response->decodeResponseJson(); $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + $this->assertTrue( + SingleOptin::where('email', 'singleoptin@hotmail.fr')->exists(), + 'Single optin should have been set for email "singleoptin@hotmail.fr"' + ); + } + + /** + * Test set single optin off + * route: apiV2.optin + * + * @return void + */ + public function testSetSingleOptinOff() + { + $service = Service::firstOrFail(); $response = $this->callApiWithToken($service, 'post', route('apiV2.optin'), [ - 'email' => 'warthogdj@hotmail.fr', + 'email' => 'singleoptin@hotmail.fr', 'opt_in' => 0 ]); $json = $response->decodeResponseJson(); $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - $this->assertNull(SingleOptin::where('email', 'warthogdj@hotmail.fr')->first()); + $this->assertFalse( + SingleOptin::where('email', 'singleoptin@hotmail.fr')->exists(), + 'Single optin should have been removed for email "singleoptin@hotmail.fr"' + ); } - public function testSingleOptinOnCustomerExsits() + /** + * Should throw an ApiError exception if trying to set + * optin to an email that already have an account. + * + * @return void + */ + public function testSingleOptinShouldFailIfCustomerExists() { $service = Service::firstOrFail(); $this->storeCustomer($service); + + // * Make sure a single optin exists. + $singleOptin = SingleOptin::whereEmail('singleoptin@hotmail.fr')->firstOrNew(); + $singleOptin->brand()->associate($service->brand); + $singleOptin->email = 'singleoptin@hotmail.fr'; + $singleOptin->saveOrFail(); + $response = $this->callApiWithToken($service, 'post', route('apiV2.optin'), [ - 'email' => 'warthogdj@hotmail.fr', + 'email' => 'singleoptin@hotmail.fr', 'opt_in' => 1 ]); @@ -53,14 +87,20 @@ class SingleOptinTest extends ApiBase $this->assertEquals(ApiError::singleOptinCustomerExists()->label, $json->json('data.message')); } + /** + * Store a customer in DB + * + * @param Service $service + * @return void + */ private function storeCustomer(Service $service) { $data = [ - 'firstname' => 'Jean', + 'firstname' => 'Jean', 'lastname' => 'Bond', 'birth_date' => \date('1992-03-08'), - 'address' => '10 impasse Maryse Bastié', - 'email' => 'warthogdj@hotmail.fr', + 'address' => '10 rue des totos', + 'email' => 'singleoptin@hotmail.fr', 'password' => '123.Mot.De.P4sse', 'phone' => '+33654821562', 'country' => (string)Country::france(), diff --git a/tests/CRM/ApiV2/StatsTest.php b/tests/CRM/ApiV2/StatsTest.php new file mode 100644 index 0000000..22496b3 --- /dev/null +++ b/tests/CRM/ApiV2/StatsTest.php @@ -0,0 +1,202 @@ +callApiWithToken($service, 'get', route('apiV2.stats.brandStats'), [ + 'start' => Carbon::now()->subMonths(3)->format('Y-m-d'), + 'end' => Carbon::now()->format('Y-m-d') + ]); + + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + $json->assertStructure([ + 'data' => [ + 'stats' => [ + '*' => [ + 'date', + 'active', + 'inactive', + 'old_accounts', + 'new_accounts', + 'total_accounts', + 'total_opt_in' + ] + ] + ] + ]); + } + + /** + * Test get stats by brand should fail with date bigger than 3 month. + * route: apiV2.stats.brandStats + * @return void + */ + public function testStatsByBrandShouldFailWithInvalidDate() + { + $service = Service::firstOrFail(); + + $response = $this->callApiWithToken($service, 'get', route('apiV2.stats.brandStats'), [ + 'start' => Carbon::now()->subMonths(3)->subDay()->format('Y-m-d'), + 'end' => Carbon::now()->format('Y-m-d') + ]); + + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals($json->json('data.code'), ApiError::validationFailed()->value); + } + + /** + * Test get stats by brand should fail with invalid date format. + * route: apiV2.stats.brandStats + * @return void + */ + public function testStatsByBrandShouldFailWithInvalidDateFormat() + { + $service = Service::firstOrFail(); + + $response = $this->callApiWithToken($service, 'get', route('apiV2.stats.brandStats'), [ + 'start' => Carbon::now()->subMonths(3)->subDay()->format('Y-m-d h:iDD'), + 'end' => Carbon::now()->format('Y-m-d') + ]); + + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals($json->json('data.code'), ApiError::validationFailed()->value); + } + + /** + * Test stats by question. + * route: apiV2.stats.questionStats + * @return void + */ + public function testStatsByQuestion() + { + $service = Service::firstOrFail(); + $question = Question::whereBrandId($service->brand->id)->firstOrFail(); + $answersIds = $question->answers()->pluck('id'); + $start = Carbon::now()->subDays(3); + $end = Carbon::now(); + + // * Make sure a customer answer is set. + /** @var \App\Models\CustomerAnswer $custAnswer */ + $custAnswer = CustomerAnswer::whereHas('answer', function ($q) use ($answersIds) { + $q->whereId($answersIds->first()); + })->firstOrNew(); + $custAnswer->customer()->associate($custAnswer->customer ?? Customer::query()->firstOrFail()); + $custAnswer->answer()->associate($answersIds->first()); + $custAnswer->saveOrFail(); + + $response = $this->callApiWithToken($service, 'get', route('apiV2.stats.questionStats', $question), [ + 'start' => $start->format('Y-m-d'), + 'end' => $end->format('Y-m-d') + ]); + + $datesIndexes = []; + + do { + $datesIndexes[$start->format('Y-m-d')] = $answersIds + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed + ->keys()->all(); + + $start = $start->addDay(); + } while ($start->lessThanOrEqualTo($end)); + + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + $json->assertStructure([ + 'data' => [ + 'answers' => [ + '*' => [ + 'id', + 'answer' + ] + ], + 'stats' => $datesIndexes + ] + ]); + } + + /** + * Test stats by question when it does not belong to brand. + * route: apiV2.stats.questionStats + * @return void + */ + public function testStatsByQuestionShouldFailWithInvalidQuestion() + { + $service = Service::firstOrFail(); + $question = Question::where('brand_id', '!=', $service->brand->id)->firstOrFail(); + + $response = $this->callApiWithToken($service, 'get', route('apiV2.stats.questionStats', $question), [ + 'start' => Carbon::now()->subMonths(3)->subDay()->format('Y-m-d'), + 'end' => Carbon::now()->format('Y-m-d') + ]); + + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals($json->json('data.code'), ApiError::validationFailed()->value); + } + + /** + * Test stats by question should fail with date bigger than 3 month. + * route: apiV2.stats.questionStats + * @return void + */ + public function testStatsByQuestionShouldFailWithInvalidDate() + { + $service = Service::firstOrFail(); + $question = Question::whereBrandId($service->brand->id)->firstOrFail(); + + $response = $this->callApiWithToken($service, 'get', route('apiV2.stats.questionStats', $question), [ + 'start' => Carbon::now()->subMonths(3)->subDay()->format('Y-m-d'), + 'end' => Carbon::now()->format('Y-m-d') + ]); + + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals($json->json('data.code'), ApiError::validationFailed()->value); + } + + /** + * Test stats by question should fail with invalid date format. + * route: apiV2.stats.questionStats + * @return void + */ + public function testStatsByQuestionShouldFailWithInvalidDateFormat() + { + $service = Service::firstOrFail(); + $question = Question::whereBrandId($service->brand->id)->firstOrFail(); + + $response = $this->callApiWithToken($service, 'get', route('apiV2.stats.questionStats', $question), [ + 'start' => Carbon::now()->subMonths(3)->format('Y-m Ddh'), + 'end' => Carbon::now()->format('Y-m-d') + ]); + + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals($json->json('data.code'), ApiError::validationFailed()->value); + } +} diff --git a/tests/Unit/CrmClientTester.php b/tests/Client/CrmClientTester.php similarity index 59% rename from tests/Unit/CrmClientTester.php rename to tests/Client/CrmClientTester.php index 416cc9f..951d88d 100644 --- a/tests/Unit/CrmClientTester.php +++ b/tests/Client/CrmClientTester.php @@ -1,34 +1,46 @@ self::$service->bearer_token]); - config(['crm.password' => self::$service->bearer_pwd]); - config(['crm.endpoint' => 'http://127.0.0.1:8888']); + new CrmApiV2( + config('app.url'), + self::$service->bearer_token, + Crypt::decryptString(self::$service->bearer_pwd) + ); + static::$apiV1 = new ApiV1(config('app.url'), self::$service->bearer_token); } /** + * Get customer + * + * @param boolean $status * @return array */ - protected static function getCustomer(bool $status = true) + protected static function getCustomer(bool $status = true): array { /** @var \App\Models\Costumer $customer */ $customer = Customer::whereHas('logins', function ($q) use ($status) { @@ -58,16 +76,9 @@ abstract class CrmClientTester extends TestCase /** @var \App\Models\Service $service */ $service = $brand->services()->firstOrFail(); - // get customer info + // Get customer info. $info = $customer->info($brand, Customer::FIRST_OR_FAIL); - // regen bearer token to get the pwd in clear - $bearer_token = $service->generateBearerToken(); - $bearer_pwd = $service->bearer_pwd; - $service->saveOrFail(); - $service->bearer_token = $bearer_token; - $service->bearer_pwd = $bearer_pwd; - return compact('customer', 'service', 'brand', 'login', 'info'); } diff --git a/tests/Unit/CrmClientV1CustomerTest.php b/tests/Client/CrmClientV1CustomerTest.php similarity index 76% rename from tests/Unit/CrmClientV1CustomerTest.php rename to tests/Client/CrmClientV1CustomerTest.php index d3faa8d..3dbd86a 100644 --- a/tests/Unit/CrmClientV1CustomerTest.php +++ b/tests/Client/CrmClientV1CustomerTest.php @@ -1,17 +1,20 @@ getCustomer(static::$customer->id); + $reponse = static::$apiV1->getCustomer(static::$customer->id); $this->assertEquals($reponse->status, 'OK'); $this->assertEquals($reponse->user->crm_id, static::$customer->id); @@ -22,26 +25,34 @@ class CrmClientV1CustomerTest extends CrmClientTester $this->assertEquals($reponse->user->address, static::$info->address); $this->assertEquals($reponse->user->address2, static::$info->address2); $this->assertEquals($reponse->user->phone, static::$info->phone); - $this->assertEquals($reponse->user->country, static::$info->country); + $this->assertEquals($reponse->user->country, static::$info->country->label); $this->assertEquals($reponse->user->city, static::$info->city); $this->assertEquals($reponse->user->opt_in, static::$info->opt_in); $this->assertEquals($reponse->user->hashcode, static::$info->hashcode); $this->assertEquals($reponse->user->status, static::$login->status); } + /** + * Get customer by id should fail if not found + * + * @return void + */ public function testGetCustomerByIdShouldFail() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->getCustomer(9999999); + $reponse = static::$apiV1->getCustomer(9999999); $this->assertEquals($reponse->status, 'KO'); $this->assertEquals($reponse->code, 101); } + /** + * Get customer by email. + * + * @return void + */ public function testGetCustomerByEmail() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->getCustomerByEmail(static::$customer->email); + $reponse = static::$apiV1->getCustomerByEmail(static::$customer->email); $this->assertEquals($reponse->status, 'OK'); $this->assertEquals($reponse->user->crm_id, static::$customer->id); @@ -52,26 +63,34 @@ class CrmClientV1CustomerTest extends CrmClientTester $this->assertEquals($reponse->user->address, static::$info->address); $this->assertEquals($reponse->user->address2, static::$info->address2); $this->assertEquals($reponse->user->phone, static::$info->phone); - $this->assertEquals($reponse->user->country, static::$info->country); + $this->assertEquals($reponse->user->country, static::$info->country->label); $this->assertEquals($reponse->user->city, static::$info->city); $this->assertEquals($reponse->user->opt_in, static::$info->opt_in); $this->assertEquals($reponse->user->hashcode, static::$info->hashcode); $this->assertEquals($reponse->user->status, static::$login->status); } + /** + * Get customer by email should fail if not found. + * + * @return void + */ public function testGetCustomerByEmailShouldFail() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->getCustomer('totoo@toto.fr'); + $reponse = static::$apiV1->getCustomer('totoo@toto.fr'); $this->assertEquals($reponse->status, 'KO'); $this->assertEquals($reponse->code, 101); } + /** + * Create customer + * + * @return void + */ public function testCustomerStoreProps() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->createCustomer( + $reponse = static::$apiV1->createCustomer( 'v1toto.delarogerie@gmail.com', 'Complic4tEd!P4assW0rd', 'toto', @@ -102,10 +121,14 @@ class CrmClientV1CustomerTest extends CrmClientTester $this->assertEquals($reponse->user->status, false); } + /** + * Create customer should fail if exists. + * + * @return void + */ public function testCustomerStorePropsShouldFailBecauseAlreadyExist() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->createCustomer( + $reponse = static::$apiV1->createCustomer( 'v1toto.delarogerie@gmail.com', 'Complic4tEd!P4assW0rd', 'toto', @@ -124,10 +147,14 @@ class CrmClientV1CustomerTest extends CrmClientTester $this->assertEquals($reponse->code, 103); } + /** + * Should fail if invalid data is passed. + * + * @return void + */ public function testCustomerStorePropsShouldFail() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->createCustomer( + $reponse = static::$apiV1->createCustomer( 'v1bistoto.delarogerie@gmail.com', '123456', 'toto bis', @@ -146,10 +173,14 @@ class CrmClientV1CustomerTest extends CrmClientTester $this->assertEquals($reponse->code, 102); } + /** + * Customer update + * + * @return void + */ public function testCustomerUpdateProps() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->updateCustomer( + $reponse = static::$apiV1->updateCustomer( ModelsCustomer::where('email', 'v1toto.delarogerie@gmail.com')->firstOrFail()->id, 'toto new', 'delarogerie', @@ -179,46 +210,66 @@ class CrmClientV1CustomerTest extends CrmClientTester $this->assertEquals($reponse->user->status, false); } + /** + * Customer update should fail if dont exists. + * + * @return void + */ public function testCustomerLoginShouldFailDontExists() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->loginCustomer('v1totso.delarogerie@gmail.com', 'Complic4tEd!P4assW0rd'); + $reponse = static::$apiV1->loginCustomer('v1totso.delarogerie@gmail.com', 'Complic4tEd!P4assW0rd'); $this->assertEquals('KO', $reponse->status); $this->assertEquals(119, $reponse->code); } + /** + * Customer update should fail if account is not activated. + * + * @return void + */ public function testCustomerLoginShouldFailNotActivated() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->loginCustomer('v1toto.delarogerie@gmail.com', 'Complic4tEd!P4assW0rd'); + $reponse = static::$apiV1->loginCustomer('v1toto.delarogerie@gmail.com', 'Complic4tEd!P4assW0rd'); $this->assertEquals('KO', $reponse->status); $this->assertEquals(116, $reponse->code); } - public function testCustomeActivate() + /** + * Customer activate + * + * @return void + */ + public function testCustomerActivate() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->activateCustomer( + $reponse = static::$apiV1->activateCustomer( ModelsCustomer::where('email', 'v1toto.delarogerie@gmail.com')->firstOrFail()->id ); $this->assertEquals('OK', $reponse->status); } + /** + * Customer login + * + * @return void + */ public function testCustomerLogin() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->loginCustomer('v1toto.delarogerie@gmail.com', 'Complic4tEd!P4assW0rd'); + $reponse = static::$apiV1->loginCustomer('v1toto.delarogerie@gmail.com', 'Complic4tEd!P4assW0rd'); $this->assertEquals('OK', $reponse->status); } - public function testCustomeActivateShouldFail() + /** + * Customer activation should fail if already exists. + * + * @return void + */ + public function testCustomerActivateShouldFail() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->activateCustomer( + $reponse = static::$apiV1->activateCustomer( ModelsCustomer::where('email', 'v1toto.delarogerie@gmail.com')->firstOrFail()->id ); @@ -226,94 +277,116 @@ class CrmClientV1CustomerTest extends CrmClientTester $this->assertEquals(110, $reponse->code); } - public function testCustomeToggleOptin() + /** + * Customer toggle optin + * + * @return void + */ + public function testCustomerToggleOptin() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->toggleOptInCustomer( + $reponse = static::$apiV1->toggleOptInCustomer( ModelsCustomer::where('email', 'v1toto.delarogerie@gmail.com')->firstOrFail()->id ); $this->assertEquals('OK', $reponse->status); - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->toggleOptInCustomer( + $reponse = static::$apiV1->toggleOptInCustomer( ModelsCustomer::where('email', 'v1toto.delarogerie@gmail.com')->firstOrFail()->id ); $this->assertEquals('OK', $reponse->status); - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->toggleOptInCustomer( + $reponse = static::$apiV1->toggleOptInCustomer( ModelsCustomer::where('email', 'v1toto.delarogerie@gmail.com')->firstOrFail()->id ); $this->assertEquals('OK', $reponse->status); } - public function testCustomeRemoveOptin() + /** + * Customer remove optin + * + * @return void + */ + public function testCustomerRemoveOptin() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $hashcode = $api->getCustomerByEmail('v1toto.delarogerie@gmail.com')->user->hashcode; - $reponse = $api->removeOptInCustomer($hashcode); + $hashcode = static::$apiV1->getCustomerByEmail('v1toto.delarogerie@gmail.com')->user->hashcode; + $reponse = static::$apiV1->removeOptInCustomer($hashcode); $this->assertEquals('OK', $reponse->status); } + /** + * Customer Delete + * + * @return void + */ public function testCustomerDelete() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->deleteCustomer(ModelsCustomer::where('email', 'v1toto.delarogerie@gmail.com') + $reponse = static::$apiV1->deleteCustomer(ModelsCustomer::where('email', 'v1toto.delarogerie@gmail.com') ->firstOrFail()->id); $this->assertNull($reponse); } + /** + * Create single optin + * + * @return void + */ public function testCreateSingleOptin() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->createSingleOptin('singlev1toto.delarogerie@gmail.com'); + $reponse = static::$apiV1->createSingleOptin('singlev1toto.delarogerie@gmail.com'); $this->assertEquals('OK', $reponse->status); } + /** + * Create single optin already exists. + * + * @return void + */ public function testCreateSingleOptinShouldFail() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->createSingleOptin('singlev1toto.delarogerie@gmail.com'); + $reponse = static::$apiV1->createSingleOptin('singlev1toto.delarogerie@gmail.com'); $this->assertEquals('KO', $reponse->status); $this->assertEquals(121, $reponse->code); } + /** + * Send arcadia outgoing mail. + * + * @return void + */ public function testCreateArcadiaOutgoingMail() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->ArcadiaOutgoingMail( + $reponse = static::$apiV1->ArcadiaOutgoingMail( ModelsCustomer::where('email', 'v1toto.delarogerie@gmail.com') ->firstOrFail()->id, [ 'root' => [ [ 'fieldName' => 'order_num', - //format 9 numeric characters + // Format 9 numeric characters. 'fieldValue' => str_pad(12, 9, '0', \STR_PAD_LEFT), ], [ 'fieldName' => 'order_date', - //format : DD/MM/YYYY + // Format : DD/MM/YYYY . 'fieldValue' => '12/05/1992', ], [ 'fieldName' => 'state_order_cb', - // format 1 ou Null (oui Null et pas NULL) + // Format 1 ou Null (oui Null et pas NULL). 'fieldValue' => 1, ], [ 'fieldName' => 'state_order_check', - // format 1 ou Null (oui Null et pas NULL) + // Format 1 ou Null (oui Null et pas NULL). 'fieldValue' => 'Null' ], [ 'fieldName' => 'operation', - // mettre valeur dans .env + // Mettre valeur dans .env . 'fieldValue' => 16 ] ], @@ -324,7 +397,7 @@ class CrmClientV1CustomerTest extends CrmClientTester ], [ 'fieldName' => 'qty', - // forced to 1 faire un appel par produit et pour chaque quantité + // Forced to 1 faire un appel par produit et pour chaque quantité. 'fieldValue' => '1' ], [ @@ -351,10 +424,14 @@ class CrmClientV1CustomerTest extends CrmClientTester $this->assertEquals('OK', $reponse->status); } + /** + * Send arcadia outgoing mail game. + * + * @return void + */ public function testCreateArcadiaOutgoingMailGameWithEan() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->ArcadiaOutgoingMail( + $reponse = static::$apiV1->ArcadiaOutgoingMail( ModelsCustomer::where('email', 'v1toto.delarogerie@gmail.com') ->firstOrFail()->id, [ @@ -401,10 +478,14 @@ class CrmClientV1CustomerTest extends CrmClientTester $this->assertEquals('OK', $reponse->status); } + /** + * Send arcadia mail without ean. + * + * @return void + */ public function testCreateArcadiaOutgoingMailGameWithoutEan() { - $api = new ApiV1(config('crm.endpoint'), config('crm.token')); - $reponse = $api->ArcadiaOutgoingMail( + $reponse = static::$apiV1->ArcadiaOutgoingMail( ModelsCustomer::where('email', 'v1toto.delarogerie@gmail.com') ->firstOrFail()->id, [ diff --git a/tests/Client/CrmClientV2ArcadiaTest.php b/tests/Client/CrmClientV2ArcadiaTest.php new file mode 100644 index 0000000..b3ac12e --- /dev/null +++ b/tests/Client/CrmClientV2ArcadiaTest.php @@ -0,0 +1,266 @@ +getCustomerOrNew(static::$info->hashcode); + + $arcadiaOrderMail = new ArcadiaMailDefault([ + 'email' => static::$customer->email, + 'firstname' => static::$info->firstname, + 'lastname' => static::$info->lastname, + 'phone' => static::$info->phone, + 'address' => static::$info->address, + 'address2' => static::$info->address2, + 'city' => static::$info->city, + 'postcode' => static::$info->postcode, + 'operation' => 16, + 'label_type_support' => 'Send Departaimants', + 'order_date' => \date('Y/m/d H:i:s'), + 'state_order' => ArcadiaOrderType::cb()->value, + 'support_code' => 1, + 'label_type_support' => 'Fond carte Aimanté', + 'order_num' => 23, + 'package_unit_weight' => 0, + ]); + $this->assertTrue($arcadiaOrderMail->send($customer)); + } + + /** + * Test arcadia default mail can serialize to json + * + * @return void + */ + public function testArcadiaOrderDefaultJson() + { + $data = [ + 'email' => static::$customer->email, + 'firstname' => static::$info->firstname, + 'lastname' => static::$info->lastname, + 'phone' => static::$info->phone, + 'address' => static::$info->address, + 'address2' => static::$info->address2, + 'city' => static::$info->city, + 'postcode' => static::$info->postcode, + 'operation' => 16, + 'label_type_support' => 'Send Departaimants', + 'order_date' => \date('Y/m/d H:i:s'), + 'state_order' => ArcadiaOrderType::cb()->value, + 'support_code' => 1, + 'label_type_support' => 'Fond carte Aimanté', + 'order_num' => 23, + 'package_unit_weight' => 0, + ]; + + $arcadiaOrderMail = new ArcadiaMailDefault($data); + + /** Mutator and accessor tests */ + foreach ($data as $propName => $value) { + $arcadiaOrderMail->{$propName} = $value; + $arcadiaOrderMail->{$propName} = $arcadiaOrderMail->{$propName}; + } + + $jsonDecoded = \json_decode(\json_encode($arcadiaOrderMail), true); + $this->assertIsArray($jsonDecoded); + foreach ($data as $key => $value) { + if (isset($jsonDecoded['deliver'][$key])) { + $this->assertArrayHasKey($key, $jsonDecoded['deliver']); + $this->assertEquals($value, $jsonDecoded['deliver'][$key]); + } else { + $this->assertArrayHasKey($key, $jsonDecoded); + $this->assertEquals($value, $jsonDecoded[$key]); + } + } + } + + /** + * Send arcadia order default game. + * + * @return void + */ + public function testArcadiaSendOrderGame() + { + $api = new CrmApiV2(); + $customer = $api->getCustomerOrNew(static::$info->hashcode); + $arcadiaOrderMail = new ArcadiaMailGame([ + 'email' => static::$customer->email, + 'firstname' => static::$info->firstname, + 'lastname' => static::$info->lastname, + 'phone' => static::$info->phone, + 'address' => static::$info->address, + 'address2' => static::$info->address2, + 'city' => static::$info->city, + 'postcode' => static::$info->postcode, + 'operation' => 16, + 'order_date' => date('Y/m/d H:i:s'), + 'support_code' => 32, + 'label_type_support' => 'Jeux Reghalal', + 'code_unique' => \strtoupper(\str_replace('-', '', (string)Str::uuid())), + 'code_on_pack' => '12365214587562' + ]); + $this->assertTrue($arcadiaOrderMail->send($customer)); + } + + /** + * Test arcadia game mail can serialize to json + * + * @return void + */ + public function testArcadiaOrderGameJson() + { + $data = [ + 'email' => static::$customer->email, + 'firstname' => static::$info->firstname, + 'lastname' => static::$info->lastname, + 'phone' => static::$info->phone, + 'address' => static::$info->address, + 'address2' => static::$info->address2, + 'city' => static::$info->city, + 'postcode' => static::$info->postcode, + 'operation' => 16, + 'order_date' => date('Y/m/d H:i:s'), + 'support_code' => 32, + 'label_type_support' => 'Jeux Reghalal', + 'code_unique' => \strtoupper(\str_replace('-', '', (string)Str::uuid())), + 'code_on_pack' => '12365214587562' + ]; + + $arcadiaGameMail = new ArcadiaMailGame($data); + + /** Mutator and accessor tests */ + foreach ($data as $propName => $value) { + $arcadiaGameMail->{$propName} = $value; + $arcadiaGameMail->{$propName} = $arcadiaGameMail->{$propName}; + } + + $jsonDecoded = \json_decode(\json_encode($arcadiaGameMail), true); + $this->assertIsArray($jsonDecoded); + foreach ($data as $key => $value) { + if (isset($jsonDecoded['deliver'][$key])) { + $this->assertArrayHasKey($key, $jsonDecoded['deliver']); + $this->assertEquals($value, $jsonDecoded['deliver'][$key]); + } else { + $this->assertArrayHasKey($key, $jsonDecoded); + $this->assertEquals($value, $jsonDecoded[$key]); + } + } + } + + /** + * Valdate xml generated files default. + * + * @return void + */ + public function testValidateGeneratedXmlDefault() + { + $fileFound = false; + + $files = File::allFiles(\storage_path('export/arcadia')); + + /** @var \Symfony\Component\Finder\SplFileInfo $file */ + foreach ($files as $file) { + if (\strpos($file->getFilename(), 'v2-order-000000023-default-') === 0) { + $fileFound = true; + + // Enable user error handling. + \libxml_use_internal_errors(true); + + $xml = new DOMDocument(); + $xml->load($file->getRealPath()); + + $this->assertTrue($xml->schemaValidate(\resource_path('documents/arcadia_default.xsd'))); + $this->libxmlDisplayErrors(); + } + } + $this->assertTrue($fileFound, 'Should have found a xml file to validate and pass validation'); + } + + /** + * Valdate xml generated files game. + * + * @return void + */ + public function testValidateGeneratedXmlGame() + { + $fileFound = false; + + $files = File::allFiles(\storage_path('export/arcadia')); + + /** @var \Symfony\Component\Finder\SplFileInfo $file */ + foreach ($files as $file) { + if (\strpos($file->getFilename(), 'v2-order-000000000-game') === 0) { + $fileFound = true; + + // Enable user error handling. + \libxml_use_internal_errors(true); + + $xml = new DOMDocument(); + $xml->load($file->getRealPath()); + + $this->assertTrue($xml->schemaValidate(\resource_path('documents/arcadia_game.xsd'))); + $this->libxmlDisplayErrors(); + } + } + $this->assertTrue($fileFound, 'Should have found a xml file to validate and pass validation'); + } + + /** + * Render a \LibXMLError + * + * @param \LibXMLError $error + * @return string + */ + private function libxmlDisplayError(\LibXMLError $error): string + { + $return = "
\n"; + switch ($error->level) { + case LIBXML_ERR_WARNING: + $return .= "Warning $error->code: "; + break; + case LIBXML_ERR_ERROR: + $return .= "Error $error->code: "; + break; + case LIBXML_ERR_FATAL: + $return .= "Fatal Error $error->code: "; + break; + } + $return .= trim($error->message); + if ($error->file) { + $return .= " in $error->file"; + } + $return .= " on line $error->line\n"; + + return $return; + } + + /** + * Display xml errors. + * + * @return void + */ + private function libxmlDisplayErrors() + { + $errors = libxml_get_errors(); + foreach ($errors as $error) { + print $this->libxmlDisplayError($error); + } + libxml_clear_errors(); + } +} diff --git a/tests/Unit/CrmClientV2ClientTest.php b/tests/Client/CrmClientV2ClientTest.php similarity index 60% rename from tests/Unit/CrmClientV2ClientTest.php rename to tests/Client/CrmClientV2ClientTest.php index 9db3b96..f74e815 100644 --- a/tests/Unit/CrmClientV2ClientTest.php +++ b/tests/Client/CrmClientV2ClientTest.php @@ -1,15 +1,22 @@ getClientRoutes(); @@ -25,9 +32,27 @@ class CrmClientV2ClientTest extends CrmClientTester $this->assertEquals(ApiStatus::OK(), $response->status); } + /** + * Test ping. + * + * @return void + */ public function testClientPing() { $api = new CrmApiV2(); $this->assertTrue($api->ping()); } + + /** + * Set unknown attribute set + * + * @return void + */ + public function testCrmModelSetUnknownAttribute() + { + $this->expectException(AccessError::class); + $customer = new Customer(); + + $customer->unknownAttribute = 'totot'; + } } diff --git a/tests/Unit/CrmClientV2ConfigTest.php b/tests/Client/CrmClientV2ConfigTest.php similarity index 67% rename from tests/Unit/CrmClientV2ConfigTest.php rename to tests/Client/CrmClientV2ConfigTest.php index a6b867c..4fb02c6 100644 --- a/tests/Unit/CrmClientV2ConfigTest.php +++ b/tests/Client/CrmClientV2ConfigTest.php @@ -1,11 +1,16 @@ assertEquals(null, Utils::getConfig('totot')); diff --git a/tests/Unit/CrmClientV2CustomerTest.php b/tests/Client/CrmClientV2CustomerTest.php similarity index 51% rename from tests/Unit/CrmClientV2CustomerTest.php rename to tests/Client/CrmClientV2CustomerTest.php index e1c348d..9b322f1 100644 --- a/tests/Unit/CrmClientV2CustomerTest.php +++ b/tests/Client/CrmClientV2CustomerTest.php @@ -1,19 +1,26 @@ expectNotToPerformAssertions(); @@ -21,6 +28,11 @@ class CrmClientV2CustomerTest extends CrmClientTester $client->email; } + /** + * Test set customer prop + * + * @return void + */ public function testSetter() { $this->expectNotToPerformAssertions(); @@ -30,22 +42,37 @@ class CrmClientV2CustomerTest extends CrmClientTester $client->firstname; } + /** + * Test client wrong type. + * + * @return void + */ public function testSetterFailWrongType() { - $this->expectException(TypeError::class); + $this->expectException(ValidationException::class); $client = new Customer(); $client->opt_in = 'toto'; } + /** + * Test country validation. + * + * @return void + */ public function testSetterFailWrongTypeCountry() { - $this->expectException(TypeError::class); + $this->expectException(ValidationException::class); $client = new Customer(); $client->country = 'toto'; } + /** + * Test set all props ok. + * + * @return void + */ public function testPropsAllSetOk() { $this->expectNotToPerformAssertions(); @@ -57,15 +84,15 @@ class CrmClientV2CustomerTest extends CrmClientTester 'firstname' => 'string', 'lastname' => 'string', 'password' => 'StrongPassword!7', - 'birthdate' => '1992-02-03', + 'birth_date' => '1992-02-03', 'phone' => '0673891630', 'address' => 'string', 'address2' => 'string', 'city' => 'string', 'postcode' => 'string', 'country' => Country::afghanistan(), - 'optin' => true, - 'status' => true, + 'opt_in' => 1, + 'status' => 1, ]; foreach ($propTypes as $prop => $type) { @@ -73,9 +100,13 @@ class CrmClientV2CustomerTest extends CrmClientTester } } - public function testPropsAllSetNotOk() + /** + * Test customer is json serializable. + * + * @return void + */ + public function testJsonSerialize() { - $this->expectException(TypeError::class); $client = new Customer(); $propTypes = [ @@ -84,14 +115,54 @@ class CrmClientV2CustomerTest extends CrmClientTester 'firstname' => 'string', 'lastname' => 'string', 'password' => 'StrongPassword!7', - 'birthdate' => '1992-02-03', + 'birth_date' => '1992-02-03', + 'phone' => '0673891630', + 'address' => 'string', + 'address2' => 'string', + 'city' => 'string', + 'postcode' => 'string', + 'country' => Country::afghanistan(), + 'opt_in' => true, + 'status' => true, + ]; + + foreach ($propTypes as $prop => $type) { + $client->{$prop} = $type; + } + $jsonDecoded = \json_decode(\json_encode($client), true); + $this->assertIsArray($jsonDecoded); + unset($propTypes['password']); + $propTypes['country'] = Country::afghanistan()->value; + foreach ($propTypes as $key => $value) { + $this->assertArrayHasKey($key, $jsonDecoded); + $this->assertEquals($value, $jsonDecoded[$key]); + } + } + + /** + * Test set all props fail. + * + * @return void + */ + public function testPropsAllSetNotOk() + { + $this->expectException(ValidationException::class); + $client = new Customer(); + + $propTypes = [ + 'hashcode' => (string)Str::uuid(), + 'email' => 'toto@gmail.com', + 'firstname' => 'string', + 'lastname' => 'string', + 'password' => 'StrongPassword!7', + 'birth_date' => '1992-02-03', 'phone' => '0673891630', 'address' => 'string', 'address2' => 'string', 'city' => 18, 'postcode' => 'string', 'country' => Country::afghanistan(), - 'optin' => true, + 'opt_in' => true, 'status' => true, ]; @@ -100,6 +171,69 @@ class CrmClientV2CustomerTest extends CrmClientTester } } + /** + * Test set unkown prop fail + * + * @return void + */ + public function testPropsAssignUnknown() + { + $this->expectException(AccessError::class); + + $customer = new Customer([ + 'hashcode' => (string)Str::uuid(), + 'email' => 'toto@gmail.com', + 'firstname' => 'string', + 'lastname' => 'string', + 'password' => 'StrongPassword!7', + 'birth_date' => '1992-02-03', + 'phone' => '0673891630', + 'address' => 'string', + 'address2' => 'string', + 'city' => 'string', + 'postcode' => 'string', + 'country' => Country::afghanistan(), + 'opt_in' => true, + 'status' => true, + ]); + $this->assertNull($customer->unknownProp); + $customer->unknownProp = 'value'; + } + + /** + * Test set prop fail wrong type + * + * @return void + */ + public function testPropsAssignWrongType() + { + $this->expectException(ValidationException::class); + + $customer = new Customer([ + 'hashcode' => (string)Str::uuid(), + 'email' => 'toto@gmail.com', + 'firstname' => 'string', + 'lastname' => 'string', + 'password' => 'StrongPassword!7', + 'birth_date' => '1992-02-03', + 'phone' => '0673891630', + 'address' => 'string', + 'address2' => 'string', + 'city' => 'string', + 'postcode' => 'string', + 'country' => Country::afghanistan(), + 'opt_in' => true, + 'status' => true, + ]); + + $customer->country = 'value'; + } + + /** + * Test get customer from hashcode. + * + * @return void + */ public function testCustomerGet() { $api = new CrmApiV2(); @@ -116,24 +250,26 @@ class CrmClientV2CustomerTest extends CrmClientTester $this->assertEquals($customer->postcode, static::$info->postcode); $this->assertEquals($customer->opt_in, static::$info->opt_in); $this->assertEquals($customer->hashcode, static::$info->hashcode); - $this->assertEquals($customer->country->label, static::$info->country); - $this->assertEquals($customer->country->value, static::$info->newcountry->value); + $this->assertEquals($customer->country->value, static::$info->country->value); + $this->assertEquals($customer->country->label, static::$info->country->label); } + /** + * Test customer activate. + * + * @return void + */ public function testCustomerActivate() { - // Get non activated customer + // Get non activated customer. /** @var \App\Models\Customer */ $customer = ModelsCustomer::whereHas('logins', function ($q) { $q->where('status', 0)->whereHas('brand', function ($q) { $q->whereId(static::$brand->id); }); })->firstOrFail(); - $login = $customer->logins()->where('status', 0)->first(); - /** @var \App\Models\Brand */ - $brand = $login->brand; - /** @var \App\Models\CustomerInfo */ - $info = $customer->info($brand, ModelsCustomer::FIRST_OR_FAIL); + + $info = $customer->info(static::$brand, ModelsCustomer::FIRST_OR_FAIL); $api = new CrmApiV2(); $customer = $api->getCustomerOrNew($info->hashcode); @@ -146,6 +282,11 @@ class CrmClientV2CustomerTest extends CrmClientTester $this->assertTrue($customer->activate()); } + /** + * Test customer update + * + * @return void + */ public function testCustomerUpdateProps() { $api = new CrmApiV2(); @@ -188,6 +329,11 @@ class CrmClientV2CustomerTest extends CrmClientTester $this->assertEquals('83160', $customer->postcode); } + /** + * Test customer toggle optin. + * + * @return void + */ public function testCustomerToggleOptin() { $api = new CrmApiV2(); @@ -206,6 +352,11 @@ class CrmClientV2CustomerTest extends CrmClientTester $this->assertTrue($customer->opt_in); } + /** + * test customer store + * + * @return void + */ public function testCustomerStoreProps() { $customer = new Customer(); @@ -227,7 +378,11 @@ class CrmClientV2CustomerTest extends CrmClientTester $this->assertEquals(null, $customer->password); } - + /** + * test customer login + * + * @return void + */ public function testCustomerLogin() { $api = new CrmApiV2(); @@ -238,6 +393,46 @@ class CrmClientV2CustomerTest extends CrmClientTester $this->assertTrue(Customer::login($customer->email, 'Complic4tEd!P4assW0rd')); } + /** + * test customer login does not belong to brand. + * + * @return void + */ + public function testCustomerLoginUsingCustomerfromAnotherBrand() + { + $this->expectException(ApiErrorException::class); + $this->expectExceptionCode(ApiError::authFailed()); + $email = Str::uuid() . 'loginfromantoherbrand@hotmail.fr'; + $data = [ + 'firstname' => 'Jean2', + 'lastname' => 'Bond2', + 'birth_date' => \date('1992-03-09'), + 'address' => '10 rue des toto', + 'email' => $email, + 'password' => '123.Mot.De.P4sseNew', + 'phone' => '+33654821563', + 'country' => Country::luxembourg()->value, + 'city' => 'Connerré2', + 'postcode' => '72161', + 'opt_in' => 0 + ]; + + $brand = Brand::where('id', '!=', static::$brand->id)->firstOrFail(); + + $this->createNewCustomer($data, $brand); + + $api = new CrmApiV2(); + $crmCustomer = $api->getCustomerOrNew($email); + $this->assertNull($crmCustomer->hashcode); + + CrmApiV2::login($email, '123.Mot.De.P4sseNew'); + } + + /** + * Test prop store fail. + * + * @return void + */ public function testCustomerStorePropsFails() { $this->expectException(ValidationException::class); @@ -259,14 +454,81 @@ class CrmClientV2CustomerTest extends CrmClientTester $this->assertEquals('toto', $customer->firstname); } + /** + * Create a new customer + * then save it into crm + * then deletes it from crm + * then try again to fetch it + * check if it exists. + * + * @return void + */ public function testCustomerDelete() { - $api = new CrmApiV2(); - $customer = $api->getCustomer('toto.delarogerie@gmail.com'); + $customer = new Customer(); + + $customer->firstname = 'toto'; + $customer->lastname = 'delarogerie'; + $customer->email = 'delete.delarogerie@gmail.com'; + $customer->password = 'Complic4tEd!P4assW0rd'; + $customer->birth_date = '1992-03-08'; + $customer->phone = '0673891160'; + $customer->address = 'rue des totos'; + $customer->city = 'Toto ville'; + $customer->postcode = '72160'; + $customer->country = Country::france(); + + $this->assertTrue($customer->save()); $this->assertTrue($customer->delete()); - $customer = $api->getCustomerOrNew('toto.delarogerie@gmail.com'); + $api = new CrmApiV2(); + $customer = $api->getCustomerOrNew('delete.delarogerie@gmail.com'); + + // * If hash code is null then it is a new customer $this->assertEquals(null, $customer->hashcode); } + + /** + * Make sute a customer exists and contains data + * + * @param array $data + * @param Brand $brand + * @return void + */ + private function createNewCustomer(array $data, Brand $brand) + { + $customer = \App\Models\Customer::whereEmail($data['email']) + ->whereHas('brands', function ($q) use ($brand) { + $q->where('brands.id', '=', $brand->id); + })->firstOrNew(); + $customer->fill($data); + $saved = $customer->save(); + + // * Delete Single optin if exists. + /** @var \App\Models\SingleOptin|null $singleOptin */ + $singleOptin = SingleOptin::where('email', $data['email']) + ->whereHas('brand', function ($q) use ($brand) { + $q->where('brands.id', '=', $brand->id); + })->first(); + if ($singleOptin) { + $saved = $singleOptin->delete() && $saved; + } + + /** @var \App\Models\CustomerInfo */ + $customerInfo = $customer->info($brand, \App\Models\Customer::FIRST_OR_NEW); + $customerInfo->fill($data); + $customerInfo->brand()->associate($brand); + $customerInfo->customer()->associate($customer); + $saved = $customerInfo->save() && $saved; + + /** @var \App\Models\CustomerLogin */ + $customerLogin = $customer->login($brand, \App\Models\Customer::FIRST_OR_NEW); + $customerLogin->fill($data); + $customerLogin->brand()->associate($brand); + $customerLogin->customer()->associate($customer); + $saved = $customerLogin->save() && $saved; + + $this->assertTrue($saved, 'A new customer should have been created.'); + } } diff --git a/tests/Client/CrmClientV2QuestionTest.php b/tests/Client/CrmClientV2QuestionTest.php new file mode 100644 index 0000000..bab560d --- /dev/null +++ b/tests/Client/CrmClientV2QuestionTest.php @@ -0,0 +1,263 @@ +assertIsArray($questions, 'Should have returned an array'); + $this->assertNotEmpty($questions, 'Should have returned some questions'); + collect($questions)->map(function ($question) { + $this->assertIsObject($question, 'Question should be an object '); + $this->assertTrue(\get_class($question) === Question::class, 'Question should be type ' . Question::class); + $this->assertIsArray($question->answers); + $this->assertNotEmpty($question->answers); + collect($question->answers)->map(function ($answer) { + $this->assertIsObject($answer, 'Answer should be an object '); + $this->assertTrue(\get_class($answer) === Answer::class, 'Question should be type ' . Answer::class); + }); + }); + } + + /** + * Test customer is json serializable. + * + * @return void + */ + public function testJsonSerialize() + { + $questions = Question::all(); + $this->assertIsArray($questions); + $this->assertTrue((bool)count($questions)); + foreach ($questions as $question) { + $jsonDecoded = \json_decode(\json_encode($question), true); + $this->assertArrayHasKey('id', $jsonDecoded); + $this->assertEquals($question->id, $jsonDecoded['id']); + $this->assertArrayHasKey('question', $jsonDecoded); + $this->assertEquals($question->question, $jsonDecoded['question']); + $this->assertArrayHasKey('type', $jsonDecoded); + $this->assertEquals($question->type->value, $jsonDecoded['type']); + $this->assertArrayHasKey('answers', $jsonDecoded); + $this->assertIsArray($jsonDecoded['answers']); + foreach ($jsonDecoded['answers'] as $key => $answer) { + $this->assertArrayHasKey('id', $answer); + $this->assertArrayHasKey('answer', $answer); + $this->assertArrayHasKey($key, $question->answers); + $this->assertEquals($question->answers[$key]->id, $answer['id']); + $this->assertEquals($question->answers[$key]->answer, $answer['answer']); + } + } + } + + /** + * Get all question with user answers + * + * @return void + */ + public function testGetAllQuestionsWithCustomerAnswers() + { + /** @var \App\Models\Question $answeredQuestion */ + $answeredQuestion = ModelsQuestion::whereHas('brand', function ($q) { + $q->whereId(static::$brand->id); + })->firstOrFail(); + + // * Make sure a customer answer is set. + $firstAnswer = $answeredQuestion->answers->first(); + $firstAnswer->load('question'); + + /** @var \App\Models\CustomerAnswer $custAnswer */ + $custAnswer = CustomerAnswer::whereHas('customer', function ($q) { + $q->whereId(static::$customer->id); + })->whereHas('answer', function ($q) use ($firstAnswer) { + $q->whereId($firstAnswer->id); + })->firstOrNew(); + + $custAnswer->customer()->associate(static::$customer); + $custAnswer->answer()->associate($firstAnswer); + $custAnswer->saveOrFail(); + + $api = new CrmApiV2(); + $customer = $api->getCustomerOrNew(static::$info->hashcode); + $questions = Question::allWithCustomerAnswers($customer); + + $this->assertIsArray($questions, 'Should have returned an array'); + $this->assertNotEmpty($questions, 'Should have returned some questions'); + $hasCheckedCustomerAnswers = false; + collect($questions)->map(function ($question) use ($answeredQuestion, &$hasCheckedCustomerAnswers) { + $this->assertIsArray($question, 'Question should be an array'); + $this->assertArrayHasKey('question', $question, 'Question should have a question key'); + $this->assertIsObject($question['question'], 'Question should be an object'); + $this->assertTrue( + \get_class($question['question']) === Question::class, + 'Question should be type ' . Question::class + ); + $this->assertIsArray($question['question']->answers); + $this->assertNotEmpty($question['question']->answers); + collect($question['question']->answers)->map(function ($answer) { + $this->assertIsObject($answer, 'Answer should be an object '); + $this->assertTrue(\get_class($answer) === Answer::class, 'Question should be type ' . Answer::class); + }); + if ($answeredQuestion->id === $question['question']->id) { + $hasCheckedCustomerAnswers = true; + $this->assertArrayHasKey('customerAnswers', $question, 'Question should have customer answers'); + $this->assertIsArray($question['customerAnswers'], 'Question should have customer answers'); + collect($question['customerAnswers'])->map(function ($custAnswer) { + $this->assertIsObject($custAnswer, 'Customer answer should be an object '); + $this->assertTrue( + \get_class($custAnswer) === Answer::class, + 'Customer answer should be type ' . Answer::class + ); + }); + } + }); + $this->assertTrue( + $hasCheckedCustomerAnswers, + 'Should have checked that response has at least on customer answer we have set on this test.' + ); + } + + /** + * Get all unanswered questions + * + * @return void + */ + public function testGetAllCustomerUnAnsweredQuestions() + { + /** @var \App\Models\Question $answeredQuestion */ + $answeredQuestion = ModelsQuestion::whereHas('brand', function ($q) { + $q->whereId(static::$brand->id); + })->firstOrFail(); + + // * Make sure a customer answer is set. + $firstAnswer = $answeredQuestion->answers->first(); + $firstAnswer->load('question'); + + /** @var \App\Models\CustomerAnswer $custAnswer */ + $custAnswer = CustomerAnswer::whereHas('customer', function ($q) { + $q->whereId(static::$customer->id); + })->whereHas('answer', function ($q) use ($firstAnswer) { + $q->whereId($firstAnswer->id); + })->firstOrNew(); + + $custAnswer->customer()->associate(static::$customer); + $custAnswer->answer()->associate($firstAnswer); + $custAnswer->saveOrFail(); + + $api = new CrmApiV2(); + $customer = $api->getCustomerOrNew(static::$info->hashcode); + $questions = Question::unansweredBy($customer); + + $this->assertIsArray($questions, 'Should have returned an array'); + $this->assertNotEmpty($questions, 'Should have returned some questions'); + collect($questions)->map(function ($question) use ($answeredQuestion) { + $this->assertIsObject($question, 'Question should be an object'); + $this->assertTrue( + \get_class($question) === Question::class, + 'Question should be type ' . Question::class + ); + $this->assertIsArray($question->answers); + $this->assertNotEmpty($question->answers); + collect($question->answers)->map(function ($answer) { + $this->assertIsObject($answer, 'Answer should be an object '); + $this->assertTrue(\get_class($answer) === Answer::class, 'Question should be type ' . Answer::class); + }); + $this->assertFalse( + $answeredQuestion->id === $question->id, + 'Questions should not have one we already answered' + ); + }); + } + + /** + * Test submit answers. + * + * @return void + */ + public function testSubmitCustomerAnswer() + { + /** @var \App\Models\Question $answeredQuestion */ + $answeredQuestion = ModelsQuestion::whereHas('brand', function ($q) { + $q->whereId(static::$brand->id); + })->inRandomOrder()->firstOrFail(); + + $api = new CrmApiV2(); + $customer = $api->getCustomerOrNew(static::$info->hashcode); + $result = Answer::setQuestionAnswers( + $customer->hashcode, + $answeredQuestion->id, + [ + $answeredQuestion->answers->get(0)->id, + $answeredQuestion->answers->get(1)->id, + ] + ); + $this->assertTrue($result); + + $api = new CrmApiV2(); + $customer = $api->getCustomerOrNew(static::$info->hashcode); + $questions = Question::unansweredBy($customer); + + $this->assertIsArray($questions, 'Should have returned an array'); + $this->assertNotEmpty($questions, 'Should have returned some questions'); + collect($questions)->map(function ($question) use ($answeredQuestion) { + $this->assertIsObject($question, 'Question should be an object'); + $this->assertTrue( + \get_class($question) === Question::class, + 'Question should be type ' . Question::class + ); + $this->assertIsArray($question->answers); + $this->assertNotEmpty($question->answers); + collect($question->answers)->map(function ($answer) { + $this->assertIsObject($answer, 'Answer should be an object '); + $this->assertTrue(\get_class($answer) === Answer::class, 'Question should be type ' . Answer::class); + }); + $this->assertFalse( + $answeredQuestion->id === $question->id, + 'Questions should not have one we already answered' + ); + }); + } + + /** + * Test unansweredBy + * + * @return void + */ + public function testGetAllCustomerUnansweredQuestionsShouldNotContainFirst() + { + $unWantedQuestion = Question::all()[0]; + + $api = new CrmApiV2(); + $customer = $api->getCustomerOrNew(static::$info->hashcode); + $questions = Question::unansweredBy($customer); + + foreach ($questions as $question) { + $this->assertFalse($unWantedQuestion->id === $question->id); + } + } + + /** + * test allWithCustomerAnswers + * + * @return void + */ + public function testGetAllQuestionsWithCustomerAnswersShouldHaveAnswers() + { + $api = new CrmApiV2(); + $customer = $api->getCustomerOrNew(static::$info->hashcode); + $questions = Question::allWithCustomerAnswers($customer); + $this->assertNotEmpty($questions[0]['customerAnswers'], 'Should have customer answers'); + } +} diff --git a/tests/Unit/CrmClientV2RouterTest.php b/tests/Client/CrmClientV2RouterTest.php similarity index 76% rename from tests/Unit/CrmClientV2RouterTest.php rename to tests/Client/CrmClientV2RouterTest.php index a2a87f6..08bf8e5 100644 --- a/tests/Unit/CrmClientV2RouterTest.php +++ b/tests/Client/CrmClientV2RouterTest.php @@ -1,12 +1,17 @@ getClientRoutes(); @@ -25,6 +30,11 @@ class CrmClientV2RouterTest extends CrmClientTester } } + /** + * Test all get routes. + * + * @return void + */ public function testGenRequest() { $this->expectNotToPerformAssertions(); @@ -33,14 +43,11 @@ class CrmClientV2RouterTest extends CrmClientTester foreach ($routes as $name => $route) { $data = []; $query = []; - if (\strpos($route['uri'], '{customer}') !== false) { - $query['customer'] = 1; + if (\strpos($route['uri'], '{serviceCustomer}') !== false) { + $query['serviceCustomer'] = 1; } - if (\strpos($route['uri'], '{question}') !== false) { - $query['question'] = 1; - } - if (\strpos($route['uri'], '{answer}') !== false) { - $query['answer'] = 1; + if (\strpos($route['uri'], '{serviceQuestion}') !== false) { + $query['serviceQuestion'] = 1; } $request = Utils::getResquest($name, $query, $data); $request->uri; diff --git a/tests/Client/CrmClientV2SingleOptinTest.php b/tests/Client/CrmClientV2SingleOptinTest.php new file mode 100644 index 0000000..7b33f37 --- /dev/null +++ b/tests/Client/CrmClientV2SingleOptinTest.php @@ -0,0 +1,64 @@ +assertTrue(SingleOptin::setStatus('singleoptinrandom@gmail.com', true)); + $this->assertTrue(ModelsSingleOptin::where('email', 'singleoptinrandom@gmail.com')->exists()); + + // Set single optin again should not fail. + $this->assertTrue(CrmApiV2::setStatus('singleoptinrandom@gmail.com', true)); + + // Remove single optin test and check. + $this->assertTrue(SingleOptin::setStatus('singleoptinrandom@gmail.com', false)); + $this->assertTrue(!ModelsSingleOptin::where('email', 'singleoptinrandom@gmail.com')->exists()); + } + + /** + * Test create single optin should fail if a customer exists. + * + * @return void + */ + public function testCreateSingleOptinShouldFailBecauseCustomerExists() + { + $this->expectException(ApiErrorException::class); + $this->expectExceptionMessage(ApiError::singleOptinCustomerExists()->label); + $customer = new Customer(); + + $randomMail = Str::uuid() . 'totosingleoptin.delarogerie@gmail.com'; + + $customer->firstname = 'toto singleoptin'; + $customer->lastname = 'delarogerie'; + $customer->email = $randomMail; + $customer->password = 'Complic4tEd!P4assW0rd'; + $customer->birth_date = '1992-03-08'; + $customer->phone = '0673891160'; + $customer->address = 'rue des totos'; + $customer->city = 'Toto ville'; + $customer->postcode = '72160'; + $customer->country = Country::france(); + + $this->assertTrue($customer->save()); + $this->assertFalse(SingleOptin::setStatus($randomMail, true)); + } +} diff --git a/tests/Unit/CrmClientV2StatsTest.php b/tests/Client/CrmClientV2StatsTest.php similarity index 85% rename from tests/Unit/CrmClientV2StatsTest.php rename to tests/Client/CrmClientV2StatsTest.php index a2d7118..f1778eb 100644 --- a/tests/Unit/CrmClientV2StatsTest.php +++ b/tests/Client/CrmClientV2StatsTest.php @@ -1,6 +1,6 @@ subMonths(3); @@ -39,6 +43,12 @@ class CrmClientV2StatsTest extends CrmClientTester } while ($start->lessThanOrEqualTo($end)); } + /** + * Get stat end date is before start date. + * + * @return void + * @throws ApiErrorException Because end date is before start date. + */ public function testStatsByBrandShouldFailIfStartDateIsAfterEndDate() { $this->expectException(ApiErrorException::class); @@ -56,6 +66,12 @@ class CrmClientV2StatsTest extends CrmClientTester } } + /** + * Test interval 3 months max. + * + * @return void + * @throws ApiErrorException Because interval is bigger thatn 3 months. + */ public function testStatsByBrandShouldFailIfIntervalIsMoreThanThreeMonths() { $this->expectException(ApiErrorException::class); @@ -73,6 +89,11 @@ class CrmClientV2StatsTest extends CrmClientTester } } + /** + * Test get stat by question. + * + * @return void + */ public function testStatsByQuestion() { /** @var \App\Models\Question $question */ @@ -102,6 +123,11 @@ class CrmClientV2StatsTest extends CrmClientTester $this->assertIsArray($stats['stats'], 'Results should be an array'); } + /** + * Get stats by question when it does not belongs to brand. + * + * @return void + */ public function testStatsByQuestionShouldFailIfQuestionDoesNotBelongsToBrand() { $this->expectException(ApiErrorException::class); @@ -119,6 +145,11 @@ class CrmClientV2StatsTest extends CrmClientTester ); } + /** + * Get stat by question when end date is before start date. + * + * @return void + */ public function testStatsByQuestionShouldFailIfStartDateIsAfterEndDate() { $this->expectException(ApiErrorException::class); @@ -136,6 +167,11 @@ class CrmClientV2StatsTest extends CrmClientTester ); } + /** + * Get stat by question when interval is more thatn 3 months. + * + * @return void + */ public function testStatsByQuestionShouldFailIfIntervalIsMoreThanThreeMonths() { $this->expectException(ApiErrorException::class); diff --git a/tests/Unit/HelperTest.php b/tests/Client/HelperTest.php similarity index 71% rename from tests/Unit/HelperTest.php rename to tests/Client/HelperTest.php index 5c0ebfc..d17e275 100644 --- a/tests/Unit/HelperTest.php +++ b/tests/Client/HelperTest.php @@ -1,6 +1,6 @@ makeRequest('apiV2.ping'); $this->assertInstanceOf(\Illuminate\Routing\Route::class, Helper::getRoute($request)); } + /** + * Helper::getRoutePrefix test + * + * @return void + */ public function testGetRoutePrefix() { $request = $this->makeRequest('apiV2.ping'); $this->assertEquals($request->route()->getPrefix(), Helper::getRoutePrefix($request)); } + /** + * Helper::getRouteName test + * + * @return void + */ public function testGetRouteName() { $request = $this->makeRequest('apiV2.ping'); $this->assertEquals($request->route()->getName(), Helper::getRouteName($request)); } + /** + * Helper::isRequestForApiV1 test + * + * @return void + */ public function testIsRequestForApiV1() { $request = $this->makeRequest('App\Http\Controllers\Api\V1\CustomerController@activateAccount'); $this->assertTrue(Helper::isRequestForApiV1($request)); } + /** + * Helper::isRequestForApiV2 test + * + * @return void + */ public function testIsRequestForApiV2() { $request = $this->makeRequest('apiV2.ping'); $this->assertTrue(Helper::isRequestForApiV2($request)); } - private function makeRequest(string $nameOrAction) + /** + * Make laravel request using name or action. + * + * @param string $nameOrAction + * @return \Illuminate\Http\Request + */ + private function makeRequest(string $nameOrAction): Request { $route = Route::getRoutes()->getByName($nameOrAction) ?? Route::getRoutes()->getByAction($nameOrAction); $request = new Request([], [], [], [], [], ['REQUEST_URI' => $route->uri()]); diff --git a/tests/Client/UtilsTest.php b/tests/Client/UtilsTest.php new file mode 100644 index 0000000..1b3f52d --- /dev/null +++ b/tests/Client/UtilsTest.php @@ -0,0 +1,79 @@ +expectNotToPerformAssertions(); + Utils::makeValidator([ + 'nullable' => null + ], ['nullable' => 'nullable'])(); + Utils::makeValidator([], ['sometimes' => 'sometimes'])(); + Utils::makeValidator([ + 'int' => 2 + ], ['int' => 'int'])(); + Utils::makeValidator([ + 'required' => 2 + ], ['required' => 'required'])(); + Utils::makeValidator([ + 'bool' => 1 + ], ['bool' => 'bool'])(); + Utils::makeValidator([ + 'bool' => 0 + ], ['bool' => 'bool'])(); + Utils::makeValidator([ + 'bool' => true + ], ['bool' => 'bool'])(); + Utils::makeValidator([ + 'uuid' => (string)Str::uuid() + ], ['uuid' => 'uuid|string'])(); + Utils::makeValidator([ + 'date' => date('Y-m-d') + ], ['date' => 'date'])(); + Utils::makeValidator([ + 'email' => 'email@gmail.com' + ], ['email' => 'email'])(); + Utils::makeValidator([ + 'array' => [] + ], ['array' => 'array'])(); + Utils::makeValidator([ + 'mixedCase' => 'azeZ' + ], ['mixedCase' => 'mixedCase'])(); + Utils::makeValidator([ + 'containDigit' => 'azeZ2' + ], ['containDigit' => 'containDigit'])(); + Utils::makeValidator([ + 'containSymbol' => 'azeZ2,' + ], ['containSymbol' => 'containSymbol'])(); + Utils::makeValidator([ + 'min' => 2, + ], ['min' => 'min:2'])(); + Utils::makeValidator([ + 'min' => 'az', + ], ['min' => 'min:2']); + Utils::makeValidator([ + 'max' => 2, + ], ['max' => 'max:2'])(); + Utils::makeValidator([ + 'max' => 'az', + ], ['max' => 'max:2'])(); + Utils::makeValidator([ + 'date_format' => date('Y-m-d H:i:s'), + ], ['date_format' => 'date_format:Y-m-d H:i:s'])(); + Utils::makeValidator([ + 'closure' => 'value', + ], ['closure' => [function ($attribute, $value, $fail) { + }]])(); + } +} diff --git a/tests/Feature/ApiV2/ApiTest.php b/tests/Feature/ApiV2/ApiTest.php deleted file mode 100644 index 940f8b3..0000000 --- a/tests/Feature/ApiV2/ApiTest.php +++ /dev/null @@ -1,60 +0,0 @@ -callApi('get', route('apiV2.ping')); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - - $response = $this->json('get', route('apiV2.ping'), ['timestamp' => time()], [ - 'X-API-TIMESTAMP' => time() + 119 - ]); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - - $response = $this->json('get', route('apiV2.ping'), ['timestamp' => time()], [ - 'X-API-TIMESTAMP' => time() + 121 - ]); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::KO()); - $json->assertFragment(['data' => [ - 'code' => ApiError::wrongTimeStamp()->value, - 'message' => ApiError::wrongTimeStamp()->label - ]]); - } - - /** - * route: apiV2.authenticate - */ - public function testApiAuthenticate() - { - extract($this->getCustomer()); - $response = $this->callApi( - 'post', - route('apiV2.authenticate'), - ['bearer_pwd' => $service->bearer_pwd], - ['Authorization' => sprintf('Bearer %s', $service->bearer_token)] - ); - - $json = $response->decodeResponseJson(); - - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - $this->assertNotEmpty($json->json('data')); - $this->assertNotEmpty($json->json('data.api_token')); - $this->assertTrue(Hash::check($json->json('data.api_token'), Service::findOrFail($service->id)->api_token)); - } -} diff --git a/tests/Feature/ApiV2/CustomerTest.php b/tests/Feature/ApiV2/CustomerTest.php deleted file mode 100644 index b622683..0000000 --- a/tests/Feature/ApiV2/CustomerTest.php +++ /dev/null @@ -1,295 +0,0 @@ -getCustomer()); - $infos = $customer->infos; - $logins = $customer->logins; - $brands = $customer->brands; - $optInHistory = $customer->optInHistory; - $this->assertInstanceOf(Collection::class, $infos); - $this->assertInstanceOf(Collection::class, $logins); - $this->assertInstanceOf(Collection::class, $brands); - $this->assertInstanceOf(Collection::class, $optInHistory); - $this->assertInstanceOf(CustomerInfo::class, $customer->info($brands->first())); - $this->assertInstanceOf(CustomerLogin::class, $customer->login($brands->first())); - } - - /** - * route: apiV2.customers.authenticate - */ - public function testApiAuthenticateCustomer() - { - extract($this->getCustomer()); - $response = $this->callApiWithToken( - $service, - 'post', - route('apiV2.customers.authenticate', []), - [ - 'email' => $customer->email, - 'password' => 'testPassword' - ] - ); - - $json = $response->decodeResponseJson(); - - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - $this->assertNotEmpty($json->json('data')); - $this->assertNotEmpty($json->json('data.firstname')); - $this->assertNotEmpty($json->json('data.lastname')); - $this->assertNotEmpty($json->json('data.birth_date')); - $this->assertNotEmpty($json->json('data.address')); - $this->assertNotEmpty($json->json('data.phone')); - $this->assertNotEmpty($json->json('data.country')); - $this->assertNotEmpty($json->json('data.city')); - $this->assertNotEmpty($json->json('data.postcode')); - $this->assertNotEmpty($json->json('data.hashcode')); - } - - /** - * route: apiV2.customers.authenticate - */ - public function testApiAuthenticateCustomerShouldFail() - { - extract($this->getCustomer()); - $response = $this->callApiWithToken( - $service, - 'post', - route('apiV2.customers.authenticate', []), - [ - 'email' => $customer->email, - 'password' => 'testPasswords' - ] - ); - - $json = $response->decodeResponseJson(); - - $this->assertApiResponseStatusIs($json, ApiStatus::KO()); - $this->assertNotEmpty($json->json('data')); - $this->assertNotEmpty($json->json('data.message')); - $this->assertNotEmpty($json->json('data.code')); - $this->assertEquals(ApiError::authFailed()->value, $json->json('data.code')); - $this->assertEquals(ApiError::authFailed()->label, $json->json('data.message')); - } - - /** - * route: apiV2.customers.store - */ - public function testApiStoreCustomer() - { - extract($this->getCustomer()); - $data = [ - 'firstname' => 'Jean', - 'lastname' => 'Bond', - 'birth_date' => \date('1992-03-08'), - 'address' => '10 impasse Maryse Bastié', - 'email' => 'warthogdj@hotmail.fr', - 'password' => '123.Mot.De.P4sse', - 'phone' => '+33654821562', - 'country' => Country::france()->value, - 'city' => 'Connerré', - 'postcode' => '72160', - 'opt_in' => true - ]; - $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $data); - $json = $response->decodeResponseJson(); - - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - $this->assertNotEmpty($json->json('data.firstname')); - $this->assertNotEmpty($json->json('data.lastname')); - $this->assertNotEmpty($json->json('data.birth_date')); - $this->assertNotEmpty($json->json('data.address')); - $this->assertNotEmpty($json->json('data.phone')); - $this->assertNotEmpty($json->json('data.country')); - $this->assertNotEmpty($json->json('data.city')); - $this->assertNotEmpty($json->json('data.postcode')); - $this->assertNotEmpty($json->json('data.hashcode')); - - $this->assertEquals(\strip_tags($data['firstname']), $json->json('data.firstname')); - $this->assertEquals($data['lastname'], $json->json('data.lastname')); - $this->assertEquals($data['birth_date'], $json->json('data.birth_date')); - $this->assertEquals($data['address'], $json->json('data.address')); - $this->assertEquals($data['phone'], $json->json('data.phone')); - $this->assertEquals($data['country'], $json->json('data.country')); - $this->assertEquals($data['city'], $json->json('data.city')); - $this->assertEquals($data['postcode'], $json->json('data.postcode')); - - $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $data); - $json = $response->decodeResponseJson(); - - $this->assertApiResponseStatusIs($json, ApiStatus::KO()); - $this->assertEquals(ApiError::customerExists()->value, $json->json('data.code')); - $this->assertEquals(ApiError::customerExists()->label, $json->json('data.message')); - - - $data['password'] = 'password'; - $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $data); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::KO()); - $this->assertEquals(ApiError::validationFailed()->value, $json->json('data.code')); - } - - /** - * route: apiV2.customers.activate - */ - public function testActivateCustomer() - { - extract($this->getCustomer(false)); - - $response = $this->callApiWithToken($service, 'post', route( - 'apiV2.customers.activate', - ['customer' => $info->hashcode] - ), []); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - - $response = $this->callApiWithToken($service, 'post', route( - 'apiV2.customers.activate', - ['customer' => $info->hashcode] - ), []); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::KO()); - $this->assertEquals(ApiError::customerAlreadyActive()->value, $json->json('data.code')); - $this->assertEquals(ApiError::customerAlreadyActive()->label, $json->json('data.message')); - } - - /** - * route: apiV2.customers.update - */ - public function testApiUpdateCustomer() - { - extract($this->getCustomer()); - $data = [ - 'firstname' => 'Jean2', - 'lastname' => 'Bond2', - 'birth_date' => \date('1992-03-09'), - 'address' => '10 impasse Maryse Bastié2', - 'email' => 'warthogdj2@hotmail.fr', - 'password' => '123.Mot.De.P4sseNew', - 'phone' => '+33654821563', - 'country' => Country::luxembourg()->value, - 'city' => 'Connerré2', - 'postcode' => '72161', - 'opt_in' => 0 - ]; - - foreach ($data as $rowName => $row) { - $response = $this->callApiWithToken($service, 'patch', route( - 'apiV2.customers.update', - ['customer' => $info->hashcode] - ), [$rowName => $row]); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - if ($rowName != 'password') { - $this->assertNotNull($json->json('data.' . $rowName), "Failed asserting $rowName is not null"); - $this->assertEquals( - $row, - $json->json('data.' . $rowName), - "Failed asserting $rowName is equal to $row" - ); - } else { - $this->assertNull($json->json('data.' . $rowName), "Failed asserting $rowName is null"); - $this->assertEquals( - null, - $json->json('data.' . $rowName), - "Failed asserting $rowName is equal to $row" - ); - } - } - $response = $this->callApiWithToken($service, 'patch', route( - 'apiV2.customers.update', - ['customer' => $info->hashcode] - ), ['address2' => 'anything']); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::KO()); - $this->assertEquals(ApiError::validationFailed()->value, $json->json('data.code')); - } - - /** - * route: apiV2.customers.show - */ - public function testApiGetCustomer() - { - extract($this->getCustomer()); - - $response = $this->callApiWithToken($service, 'get', route( - 'apiV2.customers.show', - ['customer' => $info->hashcode] - )); - $json = $response->decodeResponseJson(); - - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - $this->assertNotEmpty($json->json('data.firstname')); - $this->assertNotEmpty($json->json('data.lastname')); - $this->assertNotEmpty($json->json('data.birth_date')); - $this->assertNotEmpty($json->json('data.address')); - $this->assertNotEmpty($json->json('data.phone')); - $this->assertNotEmpty($json->json('data.country')); - $this->assertNotEmpty($json->json('data.city')); - $this->assertNotEmpty($json->json('data.postcode')); - $this->assertNotEmpty($json->json('data.hashcode')); - - $csClassName = \array_slice(explode('\\', get_class($customer)), -1)[0]; - - $uuid = Str::uuid(); - - $response = $this->callApiWithToken($service, 'get', route( - 'apiV2.customers.show', - ['customer' => $uuid] - )); - $json = $response->decodeResponseJson(); - - /** @var \App\Models\Customer */ - $customer = Customer::whereHas('infos', function ($q) use ($uuid) { - $q->where('hashcode', $uuid); - })->first(); - - $this->assertApiResponseStatusIs($json, ApiStatus::KO()); - $this->assertEquals(ApiError::modelNotFound()->value, $json->json('data.code')); - $this->assertEquals( - sprintf(ApiError::modelNotFound()->label, $csClassName), - $json->json('data.message') - ); - } - - /** - * route: apiV2.customer.destroy - */ - public function testApiDestroyCustomer() - { - extract($this->getCustomer()); - $service = $brand->services()->firstOrFail(); - $response = $this->callApiWithToken($service, 'delete', route( - 'apiV2.customers.destroy', - ['customer' => $info->hashcode] - )); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - $this->assertEmpty($json->json('data')); - - $response = $this->callApiWithToken($service, 'get', route( - 'apiV2.customers.show', - ['customer' => $info->hashcode] - )); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::KO()); - } -} diff --git a/tests/Feature/ApiV2/QuestionTest.php b/tests/Feature/ApiV2/QuestionTest.php deleted file mode 100644 index 4e072db..0000000 --- a/tests/Feature/ApiV2/QuestionTest.php +++ /dev/null @@ -1,72 +0,0 @@ -getCustomer()); - - $response = $this->callApiWithToken($service, 'get', route('apiV2.questions.index')); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - } - - /** - * route: apiV2.questions.indexWithResponse - */ - public function testQuestionIndexWithAnswers() - { - extract($this->getCustomer(true, false)); - $service = $brand->services()->firstOrFail(); - $response = $this->callApiWithToken($service, 'get', route( - 'apiV2.questions.indexWithResponse', - ['customer' => $info->hashcode] - )); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - } - - /** - * route: apiV2.questions.unAnsweredBy - */ - public function testCustomerUnAnsweredIndex() - { - extract($this->getCustomer()); - $service = $brand->services()->firstOrFail(); - $response = $this->callApiWithToken($service, 'get', route( - 'apiV2.questions.unAnsweredBy', - ['customer' => $info->hashcode] - )); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - } - - /** - * route: apiV2.customers.answers.store - */ - public function testQuestionSetAnswer() - { - extract($this->getCustomer(true, false)); - $question = Question::whereHas('brand', function ($q) use ($brand) { - $q->whereId($brand->id); - })->inRandomOrder()->firstOrFail(); - $answer = $question->answers->first(); - $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.answers.store', [ - 'customer' => $info->hashcode - ]), [ - 'question' => $question->id, - 'answers' => [$answer->id] - ]); - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - } -} diff --git a/tests/Feature/ApiV2/StatsTest.php b/tests/Feature/ApiV2/StatsTest.php deleted file mode 100644 index f40d9f4..0000000 --- a/tests/Feature/ApiV2/StatsTest.php +++ /dev/null @@ -1,89 +0,0 @@ -callApiWithToken($service, 'get', route('apiV2.stats.brandStats'), [ - 'start' => Carbon::now()->subMonths(3)->format('Y-m-d'), - 'end' => Carbon::now()->format('Y-m-d') - ]); - - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - $json->assertStructure([ - 'data' => [ - 'stats' => [ - '*' => [ - 'date', - 'active', - 'inactive', - 'old_accounts', - 'new_accounts', - 'total_accounts', - 'total_opt_in' - ] - ] - ] - ]); - } - - /** - * route: apiV2.stats.brandStats - */ - public function testStatsByQuestion() - { - $service = Service::firstOrFail(); - $question = Question::whereBrandId($service->brand->id)->firstOrFail(); - $answersIds = $question->answers()->pluck('id'); - $start = Carbon::now()->subDays(3); - $end = Carbon::now(); - - $response = $this->callApiWithToken($service, 'get', route('apiV2.stats.questionStats'), [ - 'question' => $question->id, - 'start' => $start->format('Y-m-d'), - 'end' => $end->format('Y-m-d') - ]); - - $datesIndexes = []; - - do { - $datesIndexes[$start->format('Y-m-d')] = $answersIds - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed - ->keys()->all(); - - $start = $start->addDay(); - } while ($start->lessThanOrEqualTo($end)); - - $json = $response->decodeResponseJson(); - $this->assertApiResponseStatusIs($json, ApiStatus::OK()); - $json->assertStructure([ - 'data' => [ - 'answers' => [ - '*' => [ - 'id', - 'answer' - ] - ], - 'stats' => $datesIndexes - ] - ]); - } -} diff --git a/tests/StressTest/RunMultipleRequestsTest.php b/tests/StressTest/RunMultipleRequestsTest.php new file mode 100644 index 0000000..fed7e57 --- /dev/null +++ b/tests/StressTest/RunMultipleRequestsTest.php @@ -0,0 +1,128 @@ + 'local']); + + extract(self::getCustomer()); + self::$customer = $customer; + self::$brand = $brand; + self::$service = $service; + self::$login = $login; + self::$info = $info; + + config(['crm.token' => self::$service->bearer_token]); + config(['crm.password' => self::$service->bearer_pwd]); + config(['crm.endpoint' => "http://crm.local"]); + } + + /** + * Run in a loop customer get. + * + * @return void + */ + public function testSpamSingleOptin() + { + for ($i = 35; $i >= 0; $i--) { + $this->assertTrue(SingleOptin::setStatus('singleoptinteststress@gmail.com', (bool)rand(0, 1))); + } + } + + /** + * Run in a loop customer get. + * + * @return void + */ + public function testSpamCustomerGet() + { + for ($i = 35; $i >= 0; $i--) { + $this->assertIsObject(Customer::find(self::$info->hashcode)); + } + } + + /** + * Run in a loop customer get. + * + * @return void + */ + public function testSpamQuestionsGet() + { + for ($i = 35; $i >= 0; $i--) { + $this->assertIsArray(Question::all()); + } + } + + /** + * Run in a loop customer get. + * + * @return void + */ + public function testSpamCustomerGets() + { + for ($i = 35; $i >= 0; $i--) { + $this->assertIsObject(Customer::findWithEmail(self::$customer->email)); + } + } + + /** + * @param boolean $status + * @return array + */ + protected static function getCustomer(bool $status = true) + { + /** @var \App\Models\Costumer $customer */ + $customer = ModelsCustomer::whereHas('logins', function ($q) use ($status) { + $q->where('status', $status); + })->inRandomOrder()->firstOrFail(); + /** @var \App\Models\Brand $brand */ + $login = $customer->logins()->where('status', $status)->firstOrFail(); + /** @var \App\Models\CustomerLogin $login */ + $brand = $login->brand; + /** @var \App\Models\Service $service */ + $service = $brand->services()->firstOrFail(); + + // Get customer info. + $info = $customer->info($brand, ModelsCustomer::FIRST_OR_FAIL); + + // Regen bearer token to get the pwd in clear. + $bearer_token = $service->bearer_token; + $service->saveOrFail(); + $service->bearer_token = $bearer_token; + $service->bearer_pwd = Crypt::decryptString($service->bearer_pwd); + + return compact('customer', 'service', 'brand', 'login', 'info'); + } +} diff --git a/tests/Unit/CrmClientV2ArcadiaTest.php b/tests/Unit/CrmClientV2ArcadiaTest.php deleted file mode 100644 index 4cb34f9..0000000 --- a/tests/Unit/CrmClientV2ArcadiaTest.php +++ /dev/null @@ -1,122 +0,0 @@ -getCustomerOrNew(static::$info->hashcode); - - $arcadiaOrderMail = new ArcadiaMailDefault([ - 'email' => static::$customer->email, - 'firstname' => static::$info->firstname, - 'lastname' => static::$info->lastname, - 'phone' => static::$info->phone, - 'address' => static::$info->address, - 'address2' => static::$info->address2, - 'city' => static::$info->city, - 'postcode' => static::$info->postcode, - 'operation' => 16, - 'label_type_support' => 'Send Departaimants', - 'order_date' => \date('Y/m/d H:i:s'), - 'state_order' => ArcadiaOrderType::cb()->value, - 'support_code' => 1, - 'label_type_support' => 'Fond carte Aimanté', - 'order_num' => 23, - 'package_unit_weight' => 0, - ]); - $this->assertTrue($arcadiaOrderMail->send($customer)); - } - - public function testArcadiaSendOrderGame() - { - $api = new CrmApiV2(); - $customer = $api->getCustomerOrNew(static::$info->hashcode); - $arcadiaOrderMail = new ArcadiaMailGame([ - 'email' => static::$customer->email, - 'firstname' => static::$info->firstname, - 'lastname' => static::$info->lastname, - 'phone' => static::$info->phone, - 'address' => static::$info->address, - 'address2' => static::$info->address2, - 'city' => static::$info->city, - 'postcode' => static::$info->postcode, - 'operation' => 16, - 'order_date' => date('Y/m/d H:i:s'), - 'support_code' => 32, - 'label_type_support' => 'Jeux Reghalal', - 'code_unique' => \strtoupper(\str_replace('-', '', (string)Str::uuid())), - 'code_on_pack' => '12365214587562' - ]); - $this->assertTrue($arcadiaOrderMail->send($customer)); - } - - public function testValidateGeneratedXmlGame() - { - $fileFound = false; - - $files = File::allFiles(\storage_path('export/arcadia')); - - /** @var \Symfony\Component\Finder\SplFileInfo $file */ - foreach ($files as $file) { - if (\strpos($file->getFilename(), 'v2-order-000000000-game') === 0) { - $fileFound = true; - - // Enable user error handling - \libxml_use_internal_errors(true); - - $xml = new DOMDocument(); - $xml->load($file->getRealPath()); - - if (!$xml->schemaValidate(\resource_path('documents/arcadia_game.xsd'))) { - print 'DOMDocument::schemaValidate() Generated Errors!'; - $this->libxmlDisplayErrors(); - throw new \Exception('Could not validate xml file against xsd'); - } - } - } - $this->assertTrue($fileFound, 'Should have found a xml file to validate and pass validation'); - } - - private function libxmlDisplayError($error) - { - $return = "
\n"; - switch ($error->level) { - case LIBXML_ERR_WARNING: - $return .= "Warning $error->code: "; - break; - case LIBXML_ERR_ERROR: - $return .= "Error $error->code: "; - break; - case LIBXML_ERR_FATAL: - $return .= "Fatal Error $error->code: "; - break; - } - $return .= trim($error->message); - if ($error->file) { - $return .= " in $error->file"; - } - $return .= " on line $error->line\n"; - - return $return; - } - - private function libxmlDisplayErrors() - { - $errors = libxml_get_errors(); - foreach ($errors as $error) { - print $this->libxmlDisplayError($error); - } - libxml_clear_errors(); - } -} diff --git a/tests/Unit/CrmClientV2QuestionTest.php b/tests/Unit/CrmClientV2QuestionTest.php deleted file mode 100644 index 1b40289..0000000 --- a/tests/Unit/CrmClientV2QuestionTest.php +++ /dev/null @@ -1,75 +0,0 @@ -assertNotEmpty($questions, 'Should have returned some questions'); - } - - public function testGetAllQuestionsWithCustomerAnswers() - { - $api = new CrmApiV2(); - $customer = $api->getCustomerOrNew(static::$info->hashcode); - $questions = Question::allWithCustomerAnswers($customer); - $this->assertNotEmpty($questions, 'Should have returned some questions'); - } - - public function testGetAllCustomerUnAnsweredQuestions() - { - $api = new CrmApiV2(); - $customer = $api->getCustomerOrNew(static::$info->hashcode); - $questions = Question::unansweredBy($customer); - $this->assertNotEmpty($questions, 'Should have returned some questions'); - } - - public function testSubmitCustomerAnswer() - { - $questions = Question::all(); - - $api = new CrmApiV2(); - $customer = $api->getCustomerOrNew(static::$info->hashcode); - $result = Answer::setQuestionAnswers( - $customer->hashcode, - $questions[0]->id, - [ - $questions[0]->answers[0]->id, - $questions[0]->answers[1]->id - ] - ); - $this->assertTrue($result); - } - - public function testGetAllCustomerUnansweredQuestionsShouldNotContainFirst() - { - $this->expectNotToPerformAssertions(); - $unWantedQuestion = Question::all()[0]; - - $api = new CrmApiV2(); - $customer = $api->getCustomerOrNew(static::$info->hashcode); - $questions = Question::unansweredBy($customer); - - foreach ($questions as $question) { - if ($unWantedQuestion->id === $question->id) { - throw new Exception('The question should not appear'); - } - } - } - - public function testGetAllQuestionsWithCustomerAnswersShouldHaveAnswers() - { - $api = new CrmApiV2(); - $customer = $api->getCustomerOrNew(static::$info->hashcode); - $questions = Question::allWithCustomerAnswers($customer); - $this->assertNotEmpty($questions[0]['customerAnswers'], 'Should have customer answers'); - } -} diff --git a/tests/Unit/CrmClientV2SingleOptinTest.php b/tests/Unit/CrmClientV2SingleOptinTest.php deleted file mode 100644 index ad69373..0000000 --- a/tests/Unit/CrmClientV2SingleOptinTest.php +++ /dev/null @@ -1,39 +0,0 @@ -assertTrue(SingleOptin::setStatus(static::$customer->email, true)); - } - - public function testCreateSingleOptinShouldFailBecauseCustomerExists() - { - $this->expectException(ApiErrorException::class); - $this->expectExceptionMessage(ApiError::singleOptinCustomerExists()->label); - $customer = new Customer(); - - $customer->firstname = 'toto singleoptin'; - $customer->lastname = 'delarogerie'; - $customer->email = 'totosingleoptin.delarogerie@gmail.com'; - $customer->password = 'Complic4tEd!P4assW0rd'; - $customer->birth_date = '1992-03-08'; - $customer->phone = '0673891160'; - $customer->address = 'rue des totos'; - $customer->city = 'Toto ville'; - $customer->postcode = '72160'; - $customer->country = Country::france(); - - $this->assertTrue($customer->save()); - $this->assertFalse(SingleOptin::setStatus($customer->email, true)); - } -} diff --git a/tests/coverage/Console/Commands/DeleteUnconfirmedCustomers.php.html b/tests/coverage/Console/Commands/DeleteUnconfirmedCustomers.php.html new file mode 100644 index 0000000..e8d8128 --- /dev/null +++ b/tests/coverage/Console/Commands/DeleteUnconfirmedCustomers.php.html @@ -0,0 +1,168 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Console/Commands/DeleteUnconfirmedCustomers.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
DeleteUnconfirmedCustomers
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 handle
n/a
0 / 0
3
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use App\Models\Customer;
6use App\Models\CustomerLogin;
7use Carbon\Carbon;
8use Illuminate\Console\Command;
9
10/**
11 * @codeCoverageIgnore
12 */
13class DeleteUnconfirmedCustomers extends Command
14{
15    /**
16     * The name and signature of the console command.
17     *
18     * @var string
19     */
20    protected $signature = 'customer:deleteuc';
21
22    /**
23     * The console command description.
24     *
25     * @var string
26     */
27    protected $description = 'Delete the customers that haven\'t
28    confirmed their acocunt within 24 hours after the registeration.';
29
30    /**
31     * Execute the console command.
32     *
33     * @return integer
34     */
35    public function handle()
36    {
37        $customersLogins = CustomerLogin::where([['status', 0], ['created_at', '<', Carbon::now()->subDay(1)]])->get();
38        foreach ($customersLogins as $customerLogin) {
39            // Si non activé et créer il y a plus d'un jour : Suppression.
40            $customerLogin->customer->infos()->where('brand_id', $customerLogin->brand_id)->first()->delete();
41            // On récupère son id pour le cas ou on doit le supprimer complètement après.
42            $customerId = $customerLogin->customer->id;
43            // Suppression de ses infos de login pour la marque.
44            $customerLogin->delete();
45            // Si le customer n'a plus d'info il n'a donc plus besoin de compte en crm.
46            if (count($customerLogin->customer->infos) === 0) {
47                // Suppression du customer une fois tout le reste supprimé.
48                $customer = Customer::find($customerId);
49                $customer->optInHistory()->delete();
50                $customer->delete();
51            }
52        }
53        return 0;
54    }
55}
+ + + +
+ + + + + + diff --git a/tests/coverage/Console/Commands/GenerateCrmClient.php.html b/tests/coverage/Console/Commands/GenerateCrmClient.php.html new file mode 100644 index 0000000..f6ffdef --- /dev/null +++ b/tests/coverage/Console/Commands/GenerateCrmClient.php.html @@ -0,0 +1,368 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Console/Commands/GenerateCrmClient.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
GenerateCrmClient
n/a
0 / 0
n/a
0 / 0
16
n/a
0 / 0
 handle
n/a
0 / 0
1
n/a
0 / 0
 copyApiConfigInfo
n/a
0 / 0
2
n/a
0 / 0
 copyRoutesList
n/a
0 / 0
4
n/a
0 / 0
 copyExceptions
n/a
0 / 0
1
n/a
0 / 0
 copyEnums
n/a
0 / 0
2
n/a
0 / 0
 copyToFolder
n/a
0 / 0
3
n/a
0 / 0
 replaceInPhpFile
n/a
0 / 0
3
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use App\Enums\ApiError;
6use App\Enums\ApiStatus;
7use App\Enums\ArcadiaOrderType;
8use App\Enums\Country;
9use App\Enums\QuestionType;
10use App\Exceptions\Api\V2\ApiErrorException;
11use App\Exceptions\Api\V2\CrmException;
12use Illuminate\Console\Command;
13use Illuminate\Support\Facades\File;
14use Illuminate\Support\Facades\Route;
15
16/**
17 * @codeCoverageIgnore
18 */
19class GenerateCrmClient extends Command
20{
21    private const TARGET          = 'clients/V2/src';
22    private const CLIENTNAMESPACE = 'LDC\\LDCCRM';
23
24    /**
25     * The name and signature of the console command.
26     *
27     * @var string
28     */
29    protected $signature = 'client:generate';
30
31    /**
32     * The console command description.
33     *
34     * @var string
35     */
36    protected $description = 'Generate the crm v2 client in clients/V2 folder';
37
38    /**
39     * Execute the console command.
40     *
41     * @return integer
42     */
43    public function handle()
44    {
45        $this->copyEnums();
46        $this->copyExceptions();
47        $this->copyRoutesList();
48        $this->copyApiConfigInfo();
49        return 0;
50    }
51
52    /**
53     * Copy config files
54     *
55     * @return void
56     */
57    private function copyApiConfigInfo()
58    {
59        $this->info('Copy Api Config');
60        $rootTarget = app_path('../' . self::TARGET);
61        if (File::exists("$rootTarget/../apiConfig.readonly.json")) {
62            File::delete("$rootTarget/../apiConfig.readonly.json");
63        }
64        File::put(
65            "$rootTarget/../apiConfig.readonly.json",
66            \json_encode(config('api'), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
67        );
68    }
69
70    /**
71     * Copy routes into a json file
72     *
73     * @return void
74     */
75    private function copyRoutesList()
76    {
77        $this->info('Copy Routes');
78        $routes = [];
79        /** @var \Illuminate\Routing\Route */
80        foreach (Route::getRoutes() as $route) {
81            if (\strpos($route->getPrefix(), 'v2') !== false) {
82                $routes[$route->getName()] = [
83                    'uri' => $route->uri(),
84                    'methods' => $route->methods()
85                ];
86            }
87        }
88        $rootTarget = app_path('../' . self::TARGET);
89        if (File::exists("$rootTarget/routes.json")) {
90            File::delete("$rootTarget/routes.json");
91        }
92        File::put(
93            "$rootTarget/routes.json",
94            \json_encode($routes, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
95        );
96    }
97
98    /**
99     * Copy Exeption classes
100     *
101     * @return void
102     */
103    private function copyExceptions(): void
104    {
105        $this->info('Copy Exceptions');
106        $rootTarget = app_path('../' . self::TARGET);
107        $this->copyToFolder([
108            CrmException::class,
109            ApiErrorException::class
110        ], 'Exceptions');
111        $this->replaceInPhpFile(
112            "$rootTarget/Exceptions/ApiErrorException.php",
113            'App\Enums\ApiError',
114            'LDC\LDCCRM\Enums\ApiError'
115        );
116    }
117
118    /**
119     * Copy enums classes
120     *
121     * @return void
122     */
123    private function copyEnums(): void
124    {
125        $this->info('Copy Enums');
126        $classes = [
127            ApiError::class,
128            ApiStatus::class,
129            ArcadiaOrderType::class,
130            Country::class,
131            QuestionType::class
132        ];
133        $this->copyToFolder($classes, 'Enums');
134        $rootTarget = app_path('../' . self::TARGET);
135        foreach ($classes as $class) {
136            $classBasename = class_basename($class);
137            $dest          = "$rootTarget/Enums/$classBasename.php";
138            $this->replaceInPhpFile($dest, 'BaseEnumRoutable', 'BaseEnum');
139        }
140    }
141
142    /**
143     * Copy Classes to specified folder
144     *
145     * @param array  $classes
146     * @param string $folder
147     * @return void
148     */
149    private function copyToFolder(array $classes, string $folder): void
150    {
151        $rootTarget = app_path('../' . self::TARGET);
152        foreach ($classes as $class) {
153            $classFileName = \str_replace("App\\$folder\\", '', sprintf('%s.php', $class));
154            $classFileName = \str_replace('\\', '/', $classFileName);
155            $classBasename = class_basename($class);
156            $source        = \app_path("$folder/$classFileName");
157            $dest          = "$rootTarget/$folder/$classBasename.php";
158            if (File::exists($dest)) {
159                File::delete($dest);
160            }
161            File::copy($source, $dest);
162            $this->replaceInPhpFile($dest, 'namespace App', 'namespace ' . self::CLIENTNAMESPACE);
163        }
164    }
165
166    /**
167     * Replaces a specific string with another on a file
168     *
169     * @param string $source
170     * @param string $search
171     * @param string $replace
172     * @return void
173     */
174    private function replaceInPhpFile(string $source, string $search, string $replace): void
175    {
176        $fContent = File::get($source);
177
178        // Remove trailing \Api\V2 .
179        if ($pos = strpos($fContent, '\Api\V2')) {
180            $fContent = substr_replace($fContent, '', $pos, strlen('\Api\V2'));
181        }
182
183        // Replace namespace top level.
184        $pos = strpos($fContent, $search);
185        if ($pos !== false) {
186            File::put($source, \str_replace($search, $replace, $fContent));
187        }
188    }
189}
+ + + +
+ + + + + + diff --git a/tests/coverage/Console/Commands/LdcWebStatusChecker.php.html b/tests/coverage/Console/Commands/LdcWebStatusChecker.php.html new file mode 100644 index 0000000..794f500 --- /dev/null +++ b/tests/coverage/Console/Commands/LdcWebStatusChecker.php.html @@ -0,0 +1,191 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Console/Commands/LdcWebStatusChecker.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
LdcWebStatusChecker
n/a
0 / 0
n/a
0 / 0
5
n/a
0 / 0
 handle
n/a
0 / 0
5
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use Illuminate\Console\Command;
6
7/**
8 * Command Name: LDC Web Status Checker
9 * Description: Envoi des infos Serveur/Laravel vers LDC Web
10 * Version: 1.1
11 * Author:  Vincent BRIDIER
12 * Author URI: vincent.bridier@ldc.fr
13 *
14 * @codeCoverageIgnore
15 */
16class LdcWebStatusChecker extends Command
17{
18    /**
19     * The name and signature of the console command.
20     *
21     * @var string
22     */
23    protected $signature = 'ldcweb:statuschecker';
24
25    /**
26     * The console command description.
27     *
28     * @var string
29     */
30    protected $description = 'Envoi des infos Serveur/Laravel vers LDC Web';
31
32    /**
33     * Execute the console command.
34     *
35     * @return mixed
36     */
37    public function handle()
38    {
39        $data         = [];
40        $data['uuid'] = config('ldcweb.project_uuid');
41
42        // Sitemap.xml ?
43        if (file_exists(public_path() . '/sitemap.xml')) {
44            $data['sitemap'] = 'true';
45        } else {
46            $data['sitemap'] = 'false';
47        }
48
49        // Robots.txt ?
50        if (file_exists(public_path() . '/robots.txt')) {
51            $data['robots'] = 'true';
52        } else {
53            $data['robots'] = 'false';
54        }
55
56        // Check logs.
57        $data['logs'] = 0;
58        $patherror    = storage_path('/logs');
59        $files        = glob($patherror . '/*.log');
60        if (count($files) > 0) {
61            foreach ($files as $file) {
62                $data['logs'] = $data['logs'] + filesize($file);
63            }
64        }
65
66        $data['code']['version'] = app()->version();
67
68        // Send by cURL.
69        $url     = 'https://ldc-web.ldc.fr/api/status-checker';
70        $ch      = curl_init($url);
71        $payload = ['token' => 'CE8S52Qq7stf5k8frF4T', 'datas' => json_encode($data)];
72        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
73        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
74        curl_exec($ch);
75        curl_close($ch);
76        return 0;
77    }
78}
+ + + +
+ + + + + + diff --git a/tests/coverage/Console/Commands/MigrateCrmUsers.php.html b/tests/coverage/Console/Commands/MigrateCrmUsers.php.html new file mode 100644 index 0000000..6c0df63 --- /dev/null +++ b/tests/coverage/Console/Commands/MigrateCrmUsers.php.html @@ -0,0 +1,186 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Console/Commands/MigrateCrmUsers.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
MigrateCrmUsers
n/a
0 / 0
n/a
0 / 0
6
n/a
0 / 0
 handle
n/a
0 / 0
6
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use App\Lib\MigrateOldData;
6use App\Models\Brand;
7use Exception;
8use Illuminate\Console\Command;
9use Illuminate\Support\Facades\DB;
10
11/**
12 * @codeCoverageIgnore
13 */
14class MigrateCrmUsers extends Command
15{
16    /**
17     * The name and signature of the console command.
18     *
19     * @var string
20     */
21    protected $signature = 'migrate:crm';
22
23    /**
24     * The console command description.
25     *
26     * @var string
27     */
28    protected $description = 'Migrate crm users';
29
30
31    /**
32     * Execute the console command.
33     *
34     * @return integer
35     */
36    public function handle()
37    {
38        try {
39            $table = $this->ask('What is the tmp table name with users data ?');
40
41            try {
42                DB::connection(config('database.oldDB'))->table($table)->exists();
43            } catch (Exception $e) {
44                $this->error(sprintf('%s table not found on old crm database', $table));
45                return -1;
46            }
47
48            $brandname = $this->ask('What is the brand name ?');
49            $brand     = Brand::where('name', 'LIKE', "%$brandname%")->first();
50            if (!$brand) {
51                if (!$this->confirm(sprintf('Would you like to use %s ? ', $brandname))) {
52                    $this->info('Command cancelled');
53                    return 0;
54                }
55                if ($this->confirm('Would you like to create a new one ?')) {
56                    $brand       = new Brand();
57                    $brand->name = $brandname;
58                    $brand->save();
59                } else {
60                    $this->info('Command cancelled');
61                    return 0;
62                }
63            }
64
65            $mgOldData = new MigrateOldData();
66            $mgOldData->runMigration($brand, $table);
67        } catch (Exception $e) {
68            $this->error(sprintf('Failed %s', $e));
69            return -1;
70        }//end try
71        return 0;
72    }
73}
+ + + +
+ + + + + + diff --git a/tests/coverage/Console/Commands/StartStatJobs.php.html b/tests/coverage/Console/Commands/StartStatJobs.php.html new file mode 100644 index 0000000..6630fcf --- /dev/null +++ b/tests/coverage/Console/Commands/StartStatJobs.php.html @@ -0,0 +1,150 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Console/Commands/StartStatJobs.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StartStatJobs
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 handle
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use App\Jobs\StartStatJobs as JobsStartStatJobs;
6use Illuminate\Console\Command;
7
8/**
9 * @codeCoverageIgnore
10 */
11class StartStatJobs extends Command
12{
13    /**
14     * The name and signature of the console command.
15     *
16     * @var string
17     */
18    protected $signature = 'stats:collect';
19
20    /**
21     * The console command description.
22     *
23     * @var string
24     */
25    protected $description = 'Force push the collect stats jobs into the queue (stats)';
26
27    /**
28     * Execute the console command.
29     *
30     * @return integer
31     */
32    public function handle()
33    {
34        \dispatch(new JobsStartStatJobs())->afterResponse();
35        return 0;
36    }
37}
+ + + +
+ + + + + + diff --git a/tests/coverage/Console/Commands/UserCreate.php.html b/tests/coverage/Console/Commands/UserCreate.php.html new file mode 100644 index 0000000..acc0b77 --- /dev/null +++ b/tests/coverage/Console/Commands/UserCreate.php.html @@ -0,0 +1,163 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Console/Commands/UserCreate.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
UserCreate
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 handle
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use App\Models\User;
6use Illuminate\Console\Command;
7use Illuminate\Support\Facades\Hash;
8
9/**
10 * @codeCoverageIgnore
11 */
12class UserCreate extends Command
13{
14    /**
15     * The name and signature of the console command.
16     *
17     * @var string
18     */
19    protected $signature = 'user:create';
20
21    /**
22     * The console command description.
23     *
24     * @var string
25     */
26    protected $description = 'Command description';
27
28    /**
29     * Execute the console command.
30     *
31     * @return integer
32     */
33    public function handle()
34    {
35        $firstname = $this->ask('What is the firstname?');
36        $lastname  = $this->ask('What is the lastname?');
37        $email     = $this->ask('What is the email?');
38        $pwd       = $this->secret('What is the password?');
39
40        $user            = new User();
41        $user->firstname = $firstname;
42        $user->lastname  = $lastname;
43        $user->email     = $email;
44        $user->password  = Hash::make($pwd);
45        $user->save();
46
47        $this->info('User created.');
48        return 0;
49    }
50}
+ + + +
+ + + + + + diff --git a/tests/coverage/Console/Commands/dashboard.html b/tests/coverage/Console/Commands/dashboard.html new file mode 100644 index 0000000..fb7375b --- /dev/null +++ b/tests/coverage/Console/Commands/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Console/Commands + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Console/Commands/index.html b/tests/coverage/Console/Commands/index.html new file mode 100644 index 0000000..4b5b5f1 --- /dev/null +++ b/tests/coverage/Console/Commands/index.html @@ -0,0 +1,155 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Console/Commands + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
DeleteUnconfirmedCustomers.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
GenerateCrmClient.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
LdcWebStatusChecker.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
MigrateCrmUsers.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StartStatJobs.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
UserCreate.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Console/Kernel.php.html b/tests/coverage/Console/Kernel.php.html new file mode 100644 index 0000000..5408287 --- /dev/null +++ b/tests/coverage/Console/Kernel.php.html @@ -0,0 +1,166 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Console/Kernel.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
Kernel
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 schedule
n/a
0 / 0
1
n/a
0 / 0
 commands
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console;
4
5use App\Jobs\StartStatJobs;
6use Illuminate\Console\Scheduling\Schedule;
7use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
8
9/**
10 * @codeCoverageIgnore
11 */
12class Kernel extends ConsoleKernel
13{
14    /**
15     * The Artisan commands provided by your application.
16     *
17     * @var array
18     */
19    protected $commands = [];
20
21    /**
22     * Define the application's command schedule.
23     *
24     * @param  \Illuminate\Console\Scheduling\Schedule $schedule
25     * @return void
26     */
27    protected function schedule(Schedule $schedule)
28    {
29        $schedule->job(StartStatJobs::class)->dailyAt('03:30');
30    }
31
32    /**
33     * Register the commands for the application.
34     *
35     * @return void
36     */
37    protected function commands()
38    {
39        $this->load(__DIR__ . '/Commands');
40
41        require base_path('routes/console.php');
42    }
43}
+ + + +
+ + + + + + diff --git a/tests/coverage/Console/dashboard.html b/tests/coverage/Console/dashboard.html new file mode 100644 index 0000000..a06340a --- /dev/null +++ b/tests/coverage/Console/dashboard.html @@ -0,0 +1,284 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Console + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Console/index.html b/tests/coverage/Console/index.html new file mode 100644 index 0000000..8c5c07e --- /dev/null +++ b/tests/coverage/Console/index.html @@ -0,0 +1,102 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Console + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Commands
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Kernel.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Enums/ApiError.php.html b/tests/coverage/Enums/ApiError.php.html new file mode 100644 index 0000000..3a1b321 --- /dev/null +++ b/tests/coverage/Enums/ApiError.php.html @@ -0,0 +1,287 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Enums/ApiError.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
ApiError
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 labels
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * This describes an API error.
9 *
10 * @method static self modelNotFound()
11 * The request model was not found (typically the customer)
12 *
13 * @method static self validationFailed()
14 * This means some data could not be validated,
15 * you may have to look into message to get the details
16 *
17 * @method static self customerExists()
18 * The customer already exists
19 *
20 * @method static self customerNotActivated()
21 * The customer account is not activated and cannot perform
22 * this action
23 *
24 * @method static self customerAlreadyActive()
25 * You have requested to activate a customer account,
26 * but it is already active
27 *
28 * @method static self singleOptinCustomerExists()
29 * You have requested to create a single optin for an email,
30 * but it already exists
31 *
32 * @method static self apiAuthFailed()
33 * The authentification to the api failed. You may have to
34 * check into config or .env file for your service credentials
35 *
36 * @method static self authFailed()
37 * The authentification for this customer failed because of wrong credentials
38 *
39 * @method static self methodNotAllowed()
40 * This http method is not allowed
41 *
42 * @method static self wrongTimeStamp()
43 * The timestamp given is wrong. This would happen in case if
44 * a replay attack scenario. Timestamps are provided in headers
45 * and in the http body
46 *
47 * @method static self missingParameter()
48 * The request is missing some parameters
49 *
50 * @method static self throttleRequestsException()
51 * Too many requests have been attempted. The is a throttle security.
52 * Normally you should not trigger this.
53 * If this happens fill a ticket on https://supportweb.ldc.fr
54 * with some technical details
55 *
56 * @method static self authorizationException()
57 * You are maybe trying to access data that is out of your range.
58 * This should not happen normally, it is here to allow you to handle this case.
59 * If this happens fill a ticket on https://supportweb.ldc.fr
60 * with some technical details
61 *
62 * @package LDCCrmClient
63 * @subpackage enums
64 */
65class ApiError extends BaseEnumRoutable
66{
67    /**
68     * Valeurs des possibilités de cet énumération (value)
69     *
70     * @return array
71     */
72    public static function values(): array
73    {
74        return [
75            'modelNotFound' => 101,
76            'validationFailed' => 102,
77            'customerExists' => 103,
78            'customerAlreadyActive' => 110,
79            'customerNotActivated' => 116,
80            'authFailed' => 117,
81            'apiAuthFailed' => 119,
82            'singleOptinCustomerExists' => 120,
83            'methodNotAllowed' => 150,
84            'wrongTimeStamp' => 151,
85            'missingParameter' => 400,
86            'authorizationException' => 403,
87            'throttleRequestsException' => 429
88        ];
89    }
90
91    /**
92     * Libellés des possibilités de cet énumération (label)
93     *
94     * @return array
95     */
96    public static function labels(): array
97    {
98        return [
99            'modelNotFound' => '%s introuvable.',
100            'validationFailed' => 'Impossible de valider les données',
101            'customerExists' => 'L\'utilisateur est déjà enregistré sur ce site',
102            'customerNotActivated' => 'Le compte n\'est pas activé',
103            'customerAlreadyActive' => 'Le compte est déjà activé',
104            'singleOptinCustomerExists' => 'Le single optin ne peut être ajouté car un compte existe.',
105            'authFailed' => 'Echec de l\'authentification du compte',
106            'apiAuthFailed' => 'Echec de l\'authentification client à l\'Api LDC-CRM pour ce service',
107            'methodNotAllowed' => 'This method is not allowed for this request',
108            'wrongTimeStamp' => 'Your request is malformed, please check timestamp header and parameter',
109            'missingParameter' => 'The request is missing some parameters',
110            'authorizationException' => 'You are not autorized to access this',
111            'throttleRequestsException' => 'Throttle: too many request have been attempted, please retry later'
112        ];
113    }
114}
+ + + +
+ + + + + + diff --git a/tests/coverage/Enums/ApiStatus.php.html b/tests/coverage/Enums/ApiStatus.php.html new file mode 100644 index 0000000..b7a952b --- /dev/null +++ b/tests/coverage/Enums/ApiStatus.php.html @@ -0,0 +1,117 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Enums/ApiStatus.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ApiStatus
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * @method static self OK()
9 * @method static self KO()
10 *
11 * @package LDCCrmClient
12 * @subpackage enums
13 */
14class ApiStatus extends BaseEnumRoutable
15{
16}
+ + + +
+ + + + + + diff --git a/tests/coverage/Enums/ArcadiaMailType.php.html b/tests/coverage/Enums/ArcadiaMailType.php.html new file mode 100644 index 0000000..7febf8e --- /dev/null +++ b/tests/coverage/Enums/ArcadiaMailType.php.html @@ -0,0 +1,180 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Enums/ArcadiaMailType.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ArcadiaMailType
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * @method static self default()
9 * @method static self game()
10 *
11 * @package LDCCrmClient
12 * @subpackage enums
13 */
14class ArcadiaMailType extends BaseEnumRoutable
15{
16    /**
17     * Valeurs des possibilités de cet énumération (value)
18     *
19     * @return array
20     */
21    public static function values(): array
22    {
23        return [
24            'default' => 1,
25            'game' => 2
26        ];
27    }
28}
+ + + +
+ + + + + + diff --git a/tests/coverage/Enums/ArcadiaOrderType.php.html b/tests/coverage/Enums/ArcadiaOrderType.php.html new file mode 100644 index 0000000..1b4faf2 --- /dev/null +++ b/tests/coverage/Enums/ArcadiaOrderType.php.html @@ -0,0 +1,180 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Enums/ArcadiaOrderType.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ArcadiaOrderType
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * @method static self cb()
9 * @method static self check()
10 *
11 * @package LDCCrmClient
12 * @subpackage enums
13 */
14class ArcadiaOrderType extends BaseEnumRoutable
15{
16    /**
17     * Valeurs des possibilités de cet énumération (value)
18     *
19     * @return array
20     */
21    public static function values(): array
22    {
23        return [
24            'cb' => 0,
25            'check' => 1
26        ];
27    }
28}
+ + + +
+ + + + + + diff --git a/tests/coverage/Enums/Country.php.html b/tests/coverage/Enums/Country.php.html new file mode 100644 index 0000000..47f17d8 --- /dev/null +++ b/tests/coverage/Enums/Country.php.html @@ -0,0 +1,957 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Enums/Country.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
Country
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 labels
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * Liste de Pays
9 * @source https://datahub.io/core/country-list Data Hub country list
10 * @method static self afghanistan()
11 * @method static self alandIslands()
12 * @method static self albania()
13 * @method static self algeria()
14 * @method static self americanSamoa()
15 * @method static self andorra()
16 * @method static self angola()
17 * @method static self anguilla()
18 * @method static self antarctica()
19 * @method static self antiguaAndBarbuda()
20 * @method static self argentina()
21 * @method static self armenia()
22 * @method static self aruba()
23 * @method static self australia()
24 * @method static self austria()
25 * @method static self azerbaijan()
26 * @method static self bahrain()
27 * @method static self bahamas()
28 * @method static self bangladesh()
29 * @method static self barbados()
30 * @method static self belarus()
31 * @method static self belgium()
32 * @method static self belize()
33 * @method static self benin()
34 * @method static self bermuda()
35 * @method static self bhutan()
36 * @method static self boliviaPlurinationalStateOf()
37 * @method static self bonaireSintEustatiusAndSaba()
38 * @method static self bosniaAndHerzegovina()
39 * @method static self botswana()
40 * @method static self bouvetIsland()
41 * @method static self brazil()
42 * @method static self britishIndianOceanTerritory()
43 * @method static self bruneiDarussalam()
44 * @method static self bulgaria()
45 * @method static self burkinaFaso()
46 * @method static self burundi()
47 * @method static self cambodia()
48 * @method static self cameroon()
49 * @method static self canada()
50 * @method static self capeVerde()
51 * @method static self caymanIslands()
52 * @method static self centralAfricanRepublic()
53 * @method static self chad()
54 * @method static self chile()
55 * @method static self china()
56 * @method static self christmasIsland()
57 * @method static self cocosKeelingIslands()
58 * @method static self colombia()
59 * @method static self comoros()
60 * @method static self congo()
61 * @method static self congoTheDemocraticRepublicOfThe()
62 * @method static self cookIslands()
63 * @method static self costaRica()
64 * @method static self coteDivoire()
65 * @method static self croatia()
66 * @method static self cuba()
67 * @method static self curacao()
68 * @method static self cyprus()
69 * @method static self czechRepublic()
70 * @method static self denmark()
71 * @method static self djibouti()
72 * @method static self dominica()
73 * @method static self dominicanRepublic()
74 * @method static self ecuador()
75 * @method static self egypt()
76 * @method static self elSalvador()
77 * @method static self equatorialGuinea()
78 * @method static self eritrea()
79 * @method static self estonia()
80 * @method static self ethiopia()
81 * @method static self falklandIslandsMalvinas()
82 * @method static self faroeIslands()
83 * @method static self fiji()
84 * @method static self finland()
85 * @method static self france()
86 * @method static self frenchGuiana()
87 * @method static self frenchPolynesia()
88 * @method static self frenchSouthernTerritories()
89 * @method static self gabon()
90 * @method static self gambia()
91 * @method static self georgia()
92 * @method static self germany()
93 * @method static self ghana()
94 * @method static self gibraltar()
95 * @method static self greece()
96 * @method static self greenland()
97 * @method static self grenada()
98 * @method static self guadeloupe()
99 * @method static self guam()
100 * @method static self guatemala()
101 * @method static self guernsey()
102 * @method static self guinea()
103 * @method static self guineaBissau()
104 * @method static self guyana()
105 * @method static self haiti()
106 * @method static self heardIslandAndMcdonaldIslands()
107 * @method static self holySeeVaticanCityState()
108 * @method static self honduras()
109 * @method static self hongKong()
110 * @method static self hungary()
111 * @method static self iceland()
112 * @method static self india()
113 * @method static self indonesia()
114 * @method static self iranIslamicRepublicOf()
115 * @method static self iraq()
116 * @method static self ireland()
117 * @method static self isleOfMan()
118 * @method static self israel()
119 * @method static self italy()
120 * @method static self jamaica()
121 * @method static self japan()
122 * @method static self jersey()
123 * @method static self jordan()
124 * @method static self kazakhstan()
125 * @method static self kenya()
126 * @method static self kiribati()
127 * @method static self koreaDemocraticPeoplesRepublicOf()
128 * @method static self koreaRepublicOf()
129 * @method static self kuwait()
130 * @method static self kyrgyzstan()
131 * @method static self laoPeoplesDemocraticRepublic()
132 * @method static self latvia()
133 * @method static self lebanon()
134 * @method static self lesotho()
135 * @method static self liberia()
136 * @method static self libya()
137 * @method static self liechtenstein()
138 * @method static self lithuania()
139 * @method static self luxembourg()
140 * @method static self macao()
141 * @method static self macedoniaTheFormerYugoslavRepublicOf()
142 * @method static self madagascar()
143 * @method static self malawi()
144 * @method static self malaysia()
145 * @method static self maldives()
146 * @method static self mali()
147 * @method static self malta()
148 * @method static self marshallIslands()
149 * @method static self martinique()
150 * @method static self mauritania()
151 * @method static self mauritius()
152 * @method static self mayotte()
153 * @method static self mexico()
154 * @method static self micronesiaFederatedStatesOf()
155 * @method static self moldovaRepublicOf()
156 * @method static self monaco()
157 * @method static self mongolia()
158 * @method static self montenegro()
159 * @method static self montserrat()
160 * @method static self morocco()
161 * @method static self mozambique()
162 * @method static self myanmar()
163 * @method static self namibia()
164 * @method static self nauru()
165 * @method static self nepal()
166 * @method static self netherlands()
167 * @method static self newCaledonia()
168 * @method static self newZealand()
169 * @method static self nicaragua()
170 * @method static self niger()
171 * @method static self nigeria()
172 * @method static self niue()
173 * @method static self norfolkIsland()
174 * @method static self northernMarianaIslands()
175 * @method static self norway()
176 * @method static self oman()
177 * @method static self pakistan()
178 * @method static self palau()
179 * @method static self palestineStateOf()
180 * @method static self panama()
181 * @method static self papuaNewGuinea()
182 * @method static self paraguay()
183 * @method static self peru()
184 * @method static self philippines()
185 * @method static self pitcairn()
186 * @method static self poland()
187 * @method static self portugal()
188 * @method static self puertoRico()
189 * @method static self qatar()
190 * @method static self reunion()
191 * @method static self romania()
192 * @method static self russianFederation()
193 * @method static self rwanda()
194 * @method static self saintBarthelemy()
195 * @method static self saintHelenaAscensionAndTristanDaCunha()
196 * @method static self saintKittsAndNevis()
197 * @method static self saintLucia()
198 * @method static self saintMartinFrenchPart()
199 * @method static self saintPierreAndMiquelon()
200 * @method static self saintVincentAndTheGrenadines()
201 * @method static self samoa()
202 * @method static self sanMarino()
203 * @method static self saoTomeAndPrincipe()
204 * @method static self saudiArabia()
205 * @method static self senegal()
206 * @method static self serbia()
207 * @method static self seychelles()
208 * @method static self sierraLeone()
209 * @method static self singapore()
210 * @method static self sintMaartenDutchPart()
211 * @method static self slovakia()
212 * @method static self slovenia()
213 * @method static self solomonIslands()
214 * @method static self somalia()
215 * @method static self southAfrica()
216 * @method static self southGeorgiaAndTheSouthSandwichIslands()
217 * @method static self southSudan()
218 * @method static self spain()
219 * @method static self sriLanka()
220 * @method static self sudan()
221 * @method static self suriname()
222 * @method static self svalbardAndJanMayen()
223 * @method static self swaziland()
224 * @method static self sweden()
225 * @method static self switzerland()
226 * @method static self syrianArabRepublic()
227 * @method static self taiwanProvinceOfChina()
228 * @method static self tajikistan()
229 * @method static self tanzaniaUnitedRepublicOf()
230 * @method static self thailand()
231 * @method static self timorLeste()
232 * @method static self togo()
233 * @method static self tokelau()
234 * @method static self tonga()
235 * @method static self trinidadAndTobago()
236 * @method static self tunisia()
237 * @method static self turkey()
238 * @method static self turkmenistan()
239 * @method static self turksAndCaicosIslands()
240 * @method static self tuvalu()
241 * @method static self uganda()
242 * @method static self ukraine()
243 * @method static self unitedArabEmirates()
244 * @method static self unitedKingdom()
245 * @method static self unitedStates()
246 * @method static self unitedStatesMinorOutlyingIslands()
247 * @method static self uruguay()
248 * @method static self uzbekistan()
249 * @method static self vanuatu()
250 * @method static self venezuelaBolivarianRepublicOf()
251 * @method static self vietNam()
252 * @method static self virginIslandsBritish()
253 * @method static self virginIslandsUs()
254 * @method static self wallisAndFutuna()
255 * @method static self westernSahara()
256 * @method static self yemen()
257 * @method static self zambia()
258 * @method static self zimbabwe()
259 *
260 * @package LDCCrmClient
261 * @subpackage enums
262 */
263class Country extends BaseEnumRoutable
264{
265    /**
266     * Valeurs des possibilités de cet énumération (value)
267     *
268     * @return array
269     */
270    public static function values(): array
271    {
272        return [
273            'afghanistan' => 1,
274            'alandIslands' => 2,
275            'albania' => 3,
276            'algeria' => 4,
277            'americanSamoa' => 5,
278            'andorra' => 6,
279            'angola' => 7,
280            'anguilla' => 8,
281            'antarctica' => 9,
282            'antiguaAndBarbuda' => 10,
283            'argentina' => 11,
284            'armenia' => 12,
285            'aruba' => 13,
286            'australia' => 14,
287            'austria' => 15,
288            'azerbaijan' => 16,
289            'bahrain' => 17,
290            'bahamas' => 18,
291            'bangladesh' => 19,
292            'barbados' => 20,
293            'belarus' => 21,
294            'belgium' => 22,
295            'belize' => 23,
296            'benin' => 24,
297            'bermuda' => 25,
298            'bhutan' => 26,
299            'boliviaPlurinationalStateOf' => 27,
300            'bonaireSintEustatiusAndSaba' => 28,
301            'bosniaAndHerzegovina' => 29,
302            'botswana' => 30,
303            'bouvetIsland' => 31,
304            'brazil' => 32,
305            'britishIndianOceanTerritory' => 33,
306            'bruneiDarussalam' => 34,
307            'bulgaria' => 35,
308            'burkinaFaso' => 36,
309            'burundi' => 37,
310            'cambodia' => 38,
311            'cameroon' => 39,
312            'canada' => 40,
313            'capeVerde' => 41,
314            'caymanIslands' => 42,
315            'centralAfricanRepublic' => 43,
316            'chad' => 44,
317            'chile' => 45,
318            'china' => 46,
319            'christmasIsland' => 47,
320            'cocosKeelingIslands' => 48,
321            'colombia' => 49,
322            'comoros' => 50,
323            'congo' => 51,
324            'congoTheDemocraticRepublicOfThe' => 52,
325            'cookIslands' => 53,
326            'costaRica' => 54,
327            'coteDivoire' => 55,
328            'croatia' => 56,
329            'cuba' => 57,
330            'curacao' => 58,
331            'cyprus' => 59,
332            'czechRepublic' => 60,
333            'denmark' => 61,
334            'djibouti' => 62,
335            'dominica' => 63,
336            'dominicanRepublic' => 64,
337            'ecuador' => 65,
338            'egypt' => 66,
339            'elSalvador' => 67,
340            'equatorialGuinea' => 68,
341            'eritrea' => 69,
342            'estonia' => 70,
343            'ethiopia' => 71,
344            'falklandIslandsMalvinas' => 72,
345            'faroeIslands' => 73,
346            'fiji' => 74,
347            'finland' => 75,
348            'france' => 76,
349            'frenchGuiana' => 77,
350            'frenchPolynesia' => 78,
351            'frenchSouthernTerritories' => 79,
352            'gabon' => 80,
353            'gambia' => 81,
354            'georgia' => 82,
355            'germany' => 83,
356            'ghana' => 84,
357            'gibraltar' => 85,
358            'greece' => 86,
359            'greenland' => 87,
360            'grenada' => 88,
361            'guadeloupe' => 89,
362            'guam' => 90,
363            'guatemala' => 91,
364            'guernsey' => 92,
365            'guinea' => 93,
366            'guineaBissau' => 94,
367            'guyana' => 95,
368            'haiti' => 96,
369            'heardIslandAndMcdonaldIslands' => 97,
370            'holySeeVaticanCityState' => 98,
371            'honduras' => 99,
372            'hongKong' => 100,
373            'hungary' => 101,
374            'iceland' => 102,
375            'india' => 103,
376            'indonesia' => 104,
377            'iranIslamicRepublicOf' => 105,
378            'iraq' => 106,
379            'ireland' => 107,
380            'isleOfMan' => 108,
381            'israel' => 109,
382            'italy' => 110,
383            'jamaica' => 111,
384            'japan' => 112,
385            'jersey' => 113,
386            'jordan' => 114,
387            'kazakhstan' => 115,
388            'kenya' => 116,
389            'kiribati' => 117,
390            'koreaDemocraticPeoplesRepublicOf' => 118,
391            'koreaRepublicOf' => 119,
392            'kuwait' => 120,
393            'kyrgyzstan' => 121,
394            'laoPeoplesDemocraticRepublic' => 122,
395            'latvia' => 123,
396            'lebanon' => 124,
397            'lesotho' => 125,
398            'liberia' => 126,
399            'libya' => 127,
400            'liechtenstein' => 128,
401            'lithuania' => 129,
402            'luxembourg' => 130,
403            'macao' => 131,
404            'macedoniaTheFormerYugoslavRepublicOf' => 132,
405            'madagascar' => 133,
406            'malawi' => 134,
407            'malaysia' => 135,
408            'maldives' => 136,
409            'mali' => 137,
410            'malta' => 138,
411            'marshallIslands' => 139,
412            'martinique' => 140,
413            'mauritania' => 141,
414            'mauritius' => 142,
415            'mayotte' => 143,
416            'mexico' => 144,
417            'micronesiaFederatedStatesOf' => 145,
418            'moldovaRepublicOf' => 146,
419            'monaco' => 147,
420            'mongolia' => 148,
421            'montenegro' => 149,
422            'montserrat' => 150,
423            'morocco' => 151,
424            'mozambique' => 152,
425            'myanmar' => 153,
426            'namibia' => 154,
427            'nauru' => 155,
428            'nepal' => 156,
429            'netherlands' => 157,
430            'newCaledonia' => 158,
431            'newZealand' => 159,
432            'nicaragua' => 160,
433            'niger' => 161,
434            'nigeria' => 162,
435            'niue' => 163,
436            'norfolkIsland' => 164,
437            'northernMarianaIslands' => 165,
438            'norway' => 166,
439            'oman' => 167,
440            'pakistan' => 168,
441            'palau' => 169,
442            'palestineStateOf' => 170,
443            'panama' => 171,
444            'papuaNewGuinea' => 172,
445            'paraguay' => 173,
446            'peru' => 174,
447            'philippines' => 175,
448            'pitcairn' => 176,
449            'poland' => 177,
450            'portugal' => 178,
451            'puertoRico' => 179,
452            'qatar' => 180,
453            'reunion' => 181,
454            'romania' => 182,
455            'russianFederation' => 183,
456            'rwanda' => 184,
457            'saintBarthelemy' => 185,
458            'saintHelenaAscensionAndTristanDaCunha' => 186,
459            'saintKittsAndNevis' => 187,
460            'saintLucia' => 188,
461            'saintMartinFrenchPart' => 189,
462            'saintPierreAndMiquelon' => 190,
463            'saintVincentAndTheGrenadines' => 191,
464            'samoa' => 192,
465            'sanMarino' => 193,
466            'saoTomeAndPrincipe' => 194,
467            'saudiArabia' => 195,
468            'senegal' => 196,
469            'serbia' => 197,
470            'seychelles' => 198,
471            'sierraLeone' => 199,
472            'singapore' => 200,
473            'sintMaartenDutchPart' => 201,
474            'slovakia' => 202,
475            'slovenia' => 203,
476            'solomonIslands' => 204,
477            'somalia' => 205,
478            'southAfrica' => 206,
479            'southGeorgiaAndTheSouthSandwichIslands' => 207,
480            'southSudan' => 208,
481            'spain' => 209,
482            'sriLanka' => 210,
483            'sudan' => 211,
484            'suriname' => 212,
485            'svalbardAndJanMayen' => 213,
486            'swaziland' => 214,
487            'sweden' => 215,
488            'switzerland' => 216,
489            'syrianArabRepublic' => 217,
490            'taiwanProvinceOfChina' => 218,
491            'tajikistan' => 219,
492            'tanzaniaUnitedRepublicOf' => 220,
493            'thailand' => 221,
494            'timorLeste' => 222,
495            'togo' => 223,
496            'tokelau' => 224,
497            'tonga' => 225,
498            'trinidadAndTobago' => 226,
499            'tunisia' => 227,
500            'turkey' => 228,
501            'turkmenistan' => 229,
502            'turksAndCaicosIslands' => 230,
503            'tuvalu' => 231,
504            'uganda' => 232,
505            'ukraine' => 233,
506            'unitedArabEmirates' => 234,
507            'unitedKingdom' => 235,
508            'unitedStates' => 236,
509            'unitedStatesMinorOutlyingIslands' => 237,
510            'uruguay' => 238,
511            'uzbekistan' => 239,
512            'vanuatu' => 240,
513            'venezuelaBolivarianRepublicOf' => 241,
514            'vietNam' => 242,
515            'virginIslandsBritish' => 243,
516            'virginIslandsUs' => 244,
517            'wallisAndFutuna' => 245,
518            'westernSahara' => 246,
519            'yemen' => 247,
520            'zambia' => 248,
521            'zimbabwe' => 249
522        ];
523    }
524
525    /**
526     * Libellés des possibilités de cet énumération (label)
527     *
528     * @return array
529     */
530    public static function labels(): array
531    {
532        return [
533            'afghanistan' => 'Afghanistan',
534            'alandIslands' => 'Åland Islands',
535            'albania' => 'Albania',
536            'algeria' => 'Algeria',
537            'americanSamoa' => 'American Samoa',
538            'andorra' => 'Andorra',
539            'angola' => 'Angola',
540            'anguilla' => 'Anguilla',
541            'antarctica' => 'Antarctica',
542            'antiguaAndBarbuda' => 'Antigua and Barbuda',
543            'argentina' => 'Argentina',
544            'armenia' => 'Armenia',
545            'aruba' => 'Aruba',
546            'australia' => 'Australia',
547            'austria' => 'Austria',
548            'azerbaijan' => 'Azerbaijan',
549            'bahrain' => 'Bahrain',
550            'bahamas' => 'Bahamas',
551            'bangladesh' => 'Bangladesh',
552            'barbados' => 'Barbados',
553            'belarus' => 'Belarus',
554            'belgium' => 'Belgium',
555            'belize' => 'Belize',
556            'benin' => 'Benin',
557            'bermuda' => 'Bermuda',
558            'bhutan' => 'Bhutan',
559            'boliviaPlurinationalStateOf' => 'Bolivia, Plurinational State of',
560            'bonaireSintEustatiusAndSaba' => 'Bonaire, Sint Eustatius and Saba',
561            'bosniaAndHerzegovina' => 'Bosnia and Herzegovina',
562            'botswana' => 'Botswana',
563            'bouvetIsland' => 'Bouvet Island',
564            'brazil' => 'Brazil',
565            'britishIndianOceanTerritory' => 'British Indian Ocean Territory',
566            'bruneiDarussalam' => 'Brunei Darussalam',
567            'bulgaria' => 'Bulgaria',
568            'burkinaFaso' => 'Burkina Faso',
569            'burundi' => 'Burundi',
570            'cambodia' => 'Cambodia',
571            'cameroon' => 'Cameroon',
572            'canada' => 'Canada',
573            'capeVerde' => 'Cape Verde',
574            'caymanIslands' => 'Cayman Islands',
575            'centralAfricanRepublic' => 'Central African Republic',
576            'chad' => 'Chad',
577            'chile' => 'Chile',
578            'china' => 'China',
579            'christmasIsland' => 'Christmas Island',
580            'cocosKeelingIslands' => 'Cocos (Keeling) Islands',
581            'colombia' => 'Colombia',
582            'comoros' => 'Comoros',
583            'congo' => 'Congo',
584            'congoTheDemocraticRepublicOfThe' => 'Congo, the Democratic Republic of the',
585            'cookIslands' => 'Cook Islands',
586            'costaRica' => 'Costa Rica',
587            'coteDivoire' => 'Côte d\'Ivoire',
588            'croatia' => 'Croatia',
589            'cuba' => 'Cuba',
590            'curacao' => 'Curaçao',
591            'cyprus' => 'Cyprus',
592            'czechRepublic' => 'Czech Republic',
593            'denmark' => 'Denmark',
594            'djibouti' => 'Djibouti',
595            'dominica' => 'Dominica',
596            'dominicanRepublic' => 'Dominican Republic',
597            'ecuador' => 'Ecuador',
598            'egypt' => 'Egypt',
599            'elSalvador' => 'El Salvador',
600            'equatorialGuinea' => 'Equatorial Guinea',
601            'eritrea' => 'Eritrea',
602            'estonia' => 'Estonia',
603            'ethiopia' => 'Ethiopia',
604            'falklandIslandsMalvinas' => 'Falkland Islands (Malvinas)',
605            'faroeIslands' => 'Faroe Islands',
606            'fiji' => 'Fiji',
607            'finland' => 'Finland',
608            'france' => 'France',
609            'frenchGuiana' => 'French Guiana',
610            'frenchPolynesia' => 'French Polynesia',
611            'frenchSouthernTerritories' => 'French Southern Territories',
612            'gabon' => 'Gabon',
613            'gambia' => 'Gambia',
614            'georgia' => 'Georgia',
615            'germany' => 'Germany',
616            'ghana' => 'Ghana',
617            'gibraltar' => 'Gibraltar',
618            'greece' => 'Greece',
619            'greenland' => 'Greenland',
620            'grenada' => 'Grenada',
621            'guadeloupe' => 'Guadeloupe',
622            'guam' => 'Guam',
623            'guatemala' => 'Guatemala',
624            'guernsey' => 'Guernsey',
625            'guinea' => 'Guinea',
626            'guineaBissau' => 'GuineaBissau',
627            'guyana' => 'Guyana',
628            'haiti' => 'Haiti',
629            'heardIslandAndMcdonaldIslands' => 'Heard Island and McDonald Islands',
630            'holySeeVaticanCityState' => 'Holy See (Vatican City State)',
631            'honduras' => 'Honduras',
632            'hongKong' => 'Hong Kong',
633            'hungary' => 'Hungary',
634            'iceland' => 'Iceland',
635            'india' => 'India',
636            'indonesia' => 'Indonesia',
637            'iranIslamicRepublicOf' => 'Iran, Islamic Republic of',
638            'iraq' => 'Iraq',
639            'ireland' => 'Ireland',
640            'isleOfMan' => 'Isle of Man',
641            'israel' => 'Israel',
642            'italy' => 'Italy',
643            'jamaica' => 'Jamaica',
644            'japan' => 'Japan',
645            'jersey' => 'Jersey',
646            'jordan' => 'Jordan',
647            'kazakhstan' => 'Kazakhstan',
648            'kenya' => 'Kenya',
649            'kiribati' => 'Kiribati',
650            'koreaDemocraticPeoplesRepublicOf' => 'Korea, Democratic People\'s Republic of',
651            'koreaRepublicOf' => 'Korea, Republic of',
652            'kuwait' => 'Kuwait',
653            'kyrgyzstan' => 'Kyrgyzstan',
654            'laoPeoplesDemocraticRepublic' => 'Lao People\'s Democratic Republic',
655            'latvia' => 'Latvia',
656            'lebanon' => 'Lebanon',
657            'lesotho' => 'Lesotho',
658            'liberia' => 'Liberia',
659            'libya' => 'Libya',
660            'liechtenstein' => 'Liechtenstein',
661            'lithuania' => 'Lithuania',
662            'luxembourg' => 'Luxembourg',
663            'macao' => 'Macao',
664            'macedoniaTheFormerYugoslavRepublicOf' => 'Macedonia, the Former Yugoslav Republic of',
665            'madagascar' => 'Madagascar',
666            'malawi' => 'Malawi',
667            'malaysia' => 'Malaysia',
668            'maldives' => 'Maldives',
669            'mali' => 'Mali',
670            'malta' => 'Malta',
671            'marshallIslands' => 'Marshall Islands',
672            'martinique' => 'Martinique',
673            'mauritania' => 'Mauritania',
674            'mauritius' => 'Mauritius',
675            'mayotte' => 'Mayotte',
676            'mexico' => 'Mexico',
677            'micronesiaFederatedStatesOf' => 'Micronesia, Federated States of',
678            'moldovaRepublicOf' => 'Moldova, Republic of',
679            'monaco' => 'Monaco',
680            'mongolia' => 'Mongolia',
681            'montenegro' => 'Montenegro',
682            'montserrat' => 'Montserrat',
683            'morocco' => 'Morocco',
684            'mozambique' => 'Mozambique',
685            'myanmar' => 'Myanmar',
686            'namibia' => 'Namibia',
687            'nauru' => 'Nauru',
688            'nepal' => 'Nepal',
689            'netherlands' => 'Netherlands',
690            'newCaledonia' => 'New Caledonia',
691            'newZealand' => 'New Zealand',
692            'nicaragua' => 'Nicaragua',
693            'niger' => 'Niger',
694            'nigeria' => 'Nigeria',
695            'niue' => 'Niue',
696            'norfolkIsland' => 'Norfolk Island',
697            'northernMarianaIslands' => 'Northern Mariana Islands',
698            'norway' => 'Norway',
699            'oman' => 'Oman',
700            'pakistan' => 'Pakistan',
701            'palau' => 'Palau',
702            'palestineStateOf' => 'Palestine, State of',
703            'panama' => 'Panama',
704            'papuaNewGuinea' => 'Papua New Guinea',
705            'paraguay' => 'Paraguay',
706            'peru' => 'Peru',
707            'philippines' => 'Philippines',
708            'pitcairn' => 'Pitcairn',
709            'poland' => 'Poland',
710            'portugal' => 'Portugal',
711            'puertoRico' => 'Puerto Rico',
712            'qatar' => 'Qatar',
713            'reunion' => 'Réunion',
714            'romania' => 'Romania',
715            'russianFederation' => 'Russian Federation',
716            'rwanda' => 'Rwanda',
717            'saintBarthelemy' => 'Saint Barthélemy',
718            'saintHelenaAscensionAndTristanDaCunha' => 'Saint Helena, Ascension and Tristan da Cunha',
719            'saintKittsAndNevis' => 'Saint Kitts and Nevis',
720            'saintLucia' => 'Saint Lucia',
721            'saintMartinFrenchPart' => 'Saint Martin (French part)',
722            'saintPierreAndMiquelon' => 'Saint Pierre and Miquelon',
723            'saintVincentAndTheGrenadines' => 'Saint Vincent and the Grenadines',
724            'samoa' => 'Samoa',
725            'sanMarino' => 'San Marino',
726            'saoTomeAndPrincipe' => 'Sao Tome and Principe',
727            'saudiArabia' => 'Saudi Arabia',
728            'senegal' => 'Senegal',
729            'serbia' => 'Serbia',
730            'seychelles' => 'Seychelles',
731            'sierraLeone' => 'Sierra Leone',
732            'singapore' => 'Singapore',
733            'sintMaartenDutchPart' => 'Sint Maarten (Dutch part)',
734            'slovakia' => 'Slovakia',
735            'slovenia' => 'Slovenia',
736            'solomonIslands' => 'Solomon Islands',
737            'somalia' => 'Somalia',
738            'southAfrica' => 'South Africa',
739            'southGeorgiaAndTheSouthSandwichIslands' => 'South Georgia and the South Sandwich Islands',
740            'southSudan' => 'South Sudan',
741            'spain' => 'Spain',
742            'sriLanka' => 'Sri Lanka',
743            'sudan' => 'Sudan',
744            'suriname' => 'Suriname',
745            'svalbardAndJanMayen' => 'Svalbard and Jan Mayen',
746            'swaziland' => 'Swaziland',
747            'sweden' => 'Sweden',
748            'switzerland' => 'Switzerland',
749            'syrianArabRepublic' => 'Syrian Arab Republic',
750            'taiwanProvinceOfChina' => 'Taiwan, Province of China',
751            'tajikistan' => 'Tajikistan',
752            'tanzaniaUnitedRepublicOf' => 'Tanzania, United Republic of',
753            'thailand' => 'Thailand',
754            'timorLeste' => 'TimorLeste',
755            'togo' => 'Togo',
756            'tokelau' => 'Tokelau',
757            'tonga' => 'Tonga',
758            'trinidadAndTobago' => 'Trinidad and Tobago',
759            'tunisia' => 'Tunisia',
760            'turkey' => 'Turkey',
761            'turkmenistan' => 'Turkmenistan',
762            'turksAndCaicosIslands' => 'Turks and Caicos Islands',
763            'tuvalu' => 'Tuvalu',
764            'uganda' => 'Uganda',
765            'ukraine' => 'Ukraine',
766            'unitedArabEmirates' => 'United Arab Emirates',
767            'unitedKingdom' => 'United Kingdom',
768            'unitedStates' => 'United States',
769            'unitedStatesMinorOutlyingIslands' => 'United States Minor Outlying Islands',
770            'uruguay' => 'Uruguay',
771            'uzbekistan' => 'Uzbekistan',
772            'vanuatu' => 'Vanuatu',
773            'venezuelaBolivarianRepublicOf' => 'Venezuela, Bolivarian Republic of',
774            'vietNam' => 'Viet Nam',
775            'virginIslandsBritish' => 'Virgin Islands, British',
776            'virginIslandsUs' => 'Virgin Islands, U.S.',
777            'wallisAndFutuna' => 'Wallis and Futuna',
778            'westernSahara' => 'Western Sahara',
779            'yemen' => 'Yemen',
780            'zambia' => 'Zambia',
781            'zimbabwe' => 'Zimbabwe'
782        ];
783    }
784}
+ + + +
+ + + + + + diff --git a/tests/coverage/Enums/CustomerExportType.php.html b/tests/coverage/Enums/CustomerExportType.php.html new file mode 100644 index 0000000..c87b6af --- /dev/null +++ b/tests/coverage/Enums/CustomerExportType.php.html @@ -0,0 +1,166 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Enums/CustomerExportType.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerExportType
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 values
n/a
0 / 0
1
n/a
0 / 0
 labels
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * @method static self singleOptin()
9 * @method static self customer()
10 * @method static self both()
11 *
12 * @codeCoverageIgnore
13 */
14class CustomerExportType extends BaseEnumRoutable
15{
16    /**
17     * Valeurs des possibilités de cet énumération (value)
18     *
19     * @return array
20     */
21    public static function values(): array
22    {
23        return [
24            'singleOptin' => 1,
25            'customer' => 2,
26            'both' => 3
27        ];
28    }
29
30    /**
31     * Libellés des possibilités de cet énumération (label)
32     *
33     * @return array
34     */
35    public static function labels(): array
36    {
37        return [
38            'singleOptin' => trans('Les single optins uniquement'),
39            'customer' => trans('Les clients uniquement acceptant de recevoir les newsletters'),
40            'both' => trans('La totalité des client acceptant de recevoir les newsletters')
41        ];
42    }
43}
+ + + +
+ + + + + + diff --git a/tests/coverage/Enums/QuestionType.php.html b/tests/coverage/Enums/QuestionType.php.html new file mode 100644 index 0000000..fb9b6d4 --- /dev/null +++ b/tests/coverage/Enums/QuestionType.php.html @@ -0,0 +1,182 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Enums/QuestionType.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
QuestionType
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * @method static self select()
9 * @method static self radio()
10 * @method static self checkbox()
11 *
12 * @package LDCCrmClient
13 * @subpackage enums
14 */
15class QuestionType extends BaseEnumRoutable
16{
17    /**
18     * Valeurs des possibilités de cet énumération (value)
19     *
20     * @return array
21     */
22    public static function values(): array
23    {
24        return [
25            'select' => 1,
26            'radio' => 2,
27            'checkbox' => 3
28        ];
29    }
30}
+ + + +
+ + + + + + diff --git a/tests/coverage/Enums/dashboard.html b/tests/coverage/Enums/dashboard.html new file mode 100644 index 0000000..effec9a --- /dev/null +++ b/tests/coverage/Enums/dashboard.html @@ -0,0 +1,284 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Enums + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Enums/index.html b/tests/coverage/Enums/index.html new file mode 100644 index 0000000..2b6dc3e --- /dev/null +++ b/tests/coverage/Enums/index.html @@ -0,0 +1,257 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Enums + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
ApiError.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiStatus.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ArcadiaMailType.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ArcadiaOrderType.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Country.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CustomerExportType.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
QuestionType.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/Exceptions/AbortRowImportException.php.html b/tests/coverage/Exceptions/AbortRowImportException.php.html new file mode 100644 index 0000000..0fb034a --- /dev/null +++ b/tests/coverage/Exceptions/AbortRowImportException.php.html @@ -0,0 +1,115 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Exceptions/AbortRowImportException.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AbortRowImportException
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Exceptions;
4
5use Exception;
6
7/**
8 * This class Exception is used to skip a row import from
9 * database migration (2016 -> 2020)
10 * @author Jérémy Munsch <jeremy.munsch@ldc.fr>
11 */
12class AbortRowImportException extends Exception
13{
14}
+ + + +
+ + + + + + diff --git a/tests/coverage/Exceptions/Api/V2/ApiErrorException.php.html b/tests/coverage/Exceptions/Api/V2/ApiErrorException.php.html new file mode 100644 index 0000000..53dc0be --- /dev/null +++ b/tests/coverage/Exceptions/Api/V2/ApiErrorException.php.html @@ -0,0 +1,321 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Exceptions/Api/V2/ApiErrorException.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
CRAP
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
ApiErrorException
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
5.20
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 getApiError
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 getStatusCode
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 report
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Exceptions\Api\V2;
4
5use App\Enums\ApiError;
6use Exception;
7use Illuminate\Support\Facades\Log;
8use Throwable;
9
10/**
11 * This would be thrown if any of the case
12 * referenced in \App\Enums\ApiError would
13 * occur within the crm
14 *
15 * On the client an ApiErrorException would
16 * also be throw with the ApiError corresponding to
17 * the situation
18 *
19 * @package LDCCrmClient
20 * @subpackage exceptions
21 */
22class ApiErrorException extends Exception
23{
24    /**
25     * Api Error
26     * This value makes the error more explicit.
27     * Handling these errors are normal as inputs or
28     * request may be undesired or refused by the crm.
29     *
30     * @var \App\Enums\ApiError
31     */
32    protected $error;
33
34    /**
35     * This is an explicit error message,
36     * ou may print this to the front or not depending
37     * on $error value
38     *
39     * @var string
40     */
41    protected $message;
42
43    /**
44     * This is the http code returned by the crm.
45     * This value could be used to handle errors.
46     * But please take in consideration $error first
47     *
48     * @var integer
49     */
50    protected $httpCode;
51
52    /**
53     * Create a new exception base on ApiError enum.
54     *
55     * @param ApiError       $error
56     * @param integer        $httpCode
57     * @param string         $message
58     * @param Throwable|null $previous
59     */
60    public function __construct(ApiError $error, int $httpCode = 400, string $message = '', Throwable $previous = null)
61    {
62        $this->error    = $error;
63        $this->message  = $message;
64        $this->httpCode = $httpCode;
65        parent::__construct(
66            strlen($this->message) ? $this->message : $error->label,
67            $error->value,
68            $previous
69        );
70    }
71
72    /**
73     * Get the Api Error
74     *
75     * @return ApiError
76     */
77    public function getApiError(): ApiError
78    {
79        return $this->error;
80    }
81
82    /**
83     * Get the http code
84     *
85     * @return integer
86     */
87    public function getStatusCode(): int
88    {
89        return $this->httpCode;
90    }
91
92    /**
93     * This is used by laravel only.
94     * It uses Log class to print the error.
95     *
96     * @return void
97     */
98    public function report()
99    {
100        // @codeCoverageIgnoreStart
101        Log::debug(sprintf('CRM API: %s', $this->getMessage()));
102        // @codeCoverageIgnoreEnd
103    }
104}
+ + + +
+ + + + + + diff --git a/tests/coverage/Exceptions/Api/V2/CrmException.php.html b/tests/coverage/Exceptions/Api/V2/CrmException.php.html new file mode 100644 index 0000000..431bf33 --- /dev/null +++ b/tests/coverage/Exceptions/Api/V2/CrmException.php.html @@ -0,0 +1,143 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Exceptions/Api/V2/CrmException.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CrmException
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 report
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Exceptions\Api\V2;
4
5use Exception;
6use Illuminate\Support\Facades\Log;
7
8/**
9 * This is a critical error.
10 * The request could not be handled. You may dump any value you can
11 * get before submitting a ticker to https://supportweb.ldc.fr
12 *
13 * @package LDCCrmClient
14 * @subpackage exceptions
15 * @codeCoverageIgnore
16 */
17class CrmException extends Exception
18{
19    /**
20     * This is used by laravel only.
21     * It uses Log class to print the error.
22     *
23     * @return void
24     */
25    public function report()
26    {
27        Log::critical(sprintf('CRM API: %s', $this->getMessage()));
28    }
29}
+ + + +
+ + + + + + diff --git a/tests/coverage/Exceptions/Api/V2/dashboard.html b/tests/coverage/Exceptions/Api/V2/dashboard.html new file mode 100644 index 0000000..cfc5758 --- /dev/null +++ b/tests/coverage/Exceptions/Api/V2/dashboard.html @@ -0,0 +1,290 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Exceptions/Api/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + +
ClassCoverage
App\Exceptions\Api\V2\ApiErrorException80%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + +
ClassCRAP
App\Exceptions\Api\V2\ApiErrorException5
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
getApiError0%
report0%
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Exceptions/Api/V2/index.html b/tests/coverage/Exceptions/Api/V2/index.html new file mode 100644 index 0000000..df368ad --- /dev/null +++ b/tests/coverage/Exceptions/Api/V2/index.html @@ -0,0 +1,134 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Exceptions/Api/V2 + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ApiErrorException.php
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CrmException.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Exceptions/Api/dashboard.html b/tests/coverage/Exceptions/Api/dashboard.html new file mode 100644 index 0000000..fe2559f --- /dev/null +++ b/tests/coverage/Exceptions/Api/dashboard.html @@ -0,0 +1,289 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Exceptions/Api + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + +
ClassCoverage
App\Exceptions\Api\V2\ApiErrorException80%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + +
ClassCRAP
App\Exceptions\Api\V2\ApiErrorException5
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
getApiError0%
report0%
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Exceptions/Api/index.html b/tests/coverage/Exceptions/Api/index.html new file mode 100644 index 0000000..235303e --- /dev/null +++ b/tests/coverage/Exceptions/Api/index.html @@ -0,0 +1,120 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Exceptions/Api + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
V2
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/tests/coverage/Exceptions/Handler.php.html b/tests/coverage/Exceptions/Handler.php.html new file mode 100644 index 0000000..e4999b0 --- /dev/null +++ b/tests/coverage/Exceptions/Handler.php.html @@ -0,0 +1,386 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Exceptions/Handler.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
CRAP
+
+ 90.00% covered (success) +
+
+
90.00%
36 / 40
Handler
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
14.20
+
+ 90.00% covered (success) +
+
+
90.00%
36 / 40
 register
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 render
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 handleApiV2Exception
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
9.14
+
+ 87.88% covered (warning) +
+
+
87.88%
29 / 33
 getReponse
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Exceptions;
4
5use App\Enums\ApiError;
6use App\Enums\ApiStatus;
7use App\Exceptions\Api\V2\ApiErrorException;
8use App\Http\Resources\Api\V2\ApiResponse as V2ApiResponse;
9use App\Lib\Helper;
10use Illuminate\Auth\Access\AuthorizationException;
11use Illuminate\Auth\AuthenticationException;
12use Illuminate\Database\Eloquent\ModelNotFoundException;
13use Illuminate\Http\Exceptions\ThrottleRequestsException;
14use Illuminate\Http\JsonResponse;
15use Illuminate\Http\Request;
16use Illuminate\Validation\ValidationException;
17use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;
18use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
19use Throwable;
20
21class Handler extends ExceptionHandler
22{
23    /**
24     * A list of the exception types that are not reported.
25     *
26     * @var array
27     */
28    protected $dontReport = [
29        ApiErrorException::class,
30        RedirectTo::class
31    ];
32
33    /**
34     * A list of the inputs that are never flashed for validation exceptions.
35     *
36     * @var array
37     */
38    protected $dontFlash = [
39        'password',
40        'password_confirmation',
41    ];
42
43    /**
44     * Register the exception handling callbacks for the application.
45     *
46     * @return void
47     */
48    public function register()
49    {
50    }
51
52    /**
53     * Render an exception into an HTTP response.
54     *
55     * @param  mixed      $request
56     * @param  \Throwable $e
57     * @return \Symfony\Component\HttpFoundation\Response
58     */
59    public function render($request, Throwable $e)
60    {
61        if (Helper::isRequestForApiV2($request) and $request->isJson()) {
62            return $this->handleApiV2Exception($request, $e);
63        } //end if
64        // @codeCoverageIgnoreStart
65        return parent::render($request, $e);
66        // @codeCoverageIgnoreEnd
67    }
68
69    /**
70     * Generate a json response for any apiV2 error case
71     *
72     * @param Request    $request
73     * @param \Throwable $e
74     * @return JsonResponse
75     */
76    private function handleApiV2Exception(Request $request, \Throwable $e): JsonResponse
77    {
78        switch (get_class($e)) {
79            case AuthenticationException::class:
80                /** @var AuthenticationException $e */
81                return $this->getReponse(
82                    $request,
83                    401,
84                    ApiError::apiAuthFailed()->value,
85                    sprintf('%s, %s', ApiError::apiAuthFailed()->label, $e->getMessage())
86                );
87            case MethodNotAllowedHttpException::class:
88                /** @var MethodNotAllowedHttpException $e */
89                return $this->getReponse(
90                    $request,
91                    $e->getStatusCode(),
92                    ApiError::methodNotAllowed()->value,
93                    sprintf('%s, %s', ApiError::methodNotAllowed()->label, $e->getMessage())
94                );
95            case ValidationException::class:
96                /** @var ValidationException $e */
97                return $this->getReponse(
98                    $request,
99                    $e->status,
100                    ApiError::validationFailed()->value,
101                    sprintf(ApiError::validationFailed()->label, $e->getMessage()),
102                    ['errors' => $e->errors()]
103                );
104            case ModelNotFoundException::class:
105                /** @var ModelNotFoundException $e */
106                return $this->getReponse(
107                    $request,
108                    404,
109                    ApiError::modelNotFound()->value,
110                    sprintf(
111                        ApiError::modelNotFound()->label,
112                        \array_slice(explode('\\', $e->getModel()), -1)[0]
113                    )
114                );
115            case ApiErrorException::class:
116                /** @var ApiErrorException $e */
117                return $this->getReponse(
118                    $request,
119                    $e->getStatusCode(),
120                    $e->getCode(),
121                    $e->getMessage()
122                );
123            case ThrottleRequestsException::class:
124                /** @var ThrottleRequestsException $e */
125                return $this->getReponse(
126                    $request,
127                    429,
128                    ApiError::throttleRequestsException()->value,
129                    ApiError::throttleRequestsException()->label
130                );
131            case AuthorizationException::class:
132                /** @var AuthorizationException $e */
133                return $this->getReponse(
134                    $request,
135                    403,
136                    ApiError::authorizationException()->value,
137                    ApiError::authorizationException()->label
138                );
139        } //end switch
140        // @codeCoverageIgnoreStart
141        if (config('app.env') === 'testing') {
142            // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Found
143            print_r((string)$e);
144        }
145        return $this->getReponse($request, 500, $e->getCode(), $e->getMessage());
146        // @codeCoverageIgnoreEnd
147    }
148
149    /**
150     * Generate an ApiV2 json response
151     *
152     * @param Request $request
153     * @param integer $httpCode
154     * @param integer $apiCode
155     * @param string  $message
156     * @param array   $data
157     * @return JsonResponse
158     */
159    private static function getReponse(
160        Request $request,
161        int $httpCode,
162        int $apiCode,
163        string $message,
164        array $data = []
165    ): JsonResponse {
166        return (new V2ApiResponse(
167            (object)array_merge($data, ['message' => $message, 'code' => $apiCode]),
168            ApiStatus::KO()
169        ))->toResponse($request)->setStatusCode($httpCode);
170    }
171}
+ + + +
+ + + + + + diff --git a/tests/coverage/Exceptions/dashboard.html b/tests/coverage/Exceptions/dashboard.html new file mode 100644 index 0000000..e0f33dc --- /dev/null +++ b/tests/coverage/Exceptions/dashboard.html @@ -0,0 +1,290 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Exceptions + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + +
ClassCoverage
App\Exceptions\Api\V2\ApiErrorException80%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + +
ClassCRAP
App\Exceptions\Api\V2\ApiErrorException5
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + +
MethodCoverage
getApiError0%
report0%
handleApiV2Exception87%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + +
MethodCRAP
handleApiV2Exception9
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Exceptions/index.html b/tests/coverage/Exceptions/index.html new file mode 100644 index 0000000..2db819b --- /dev/null +++ b/tests/coverage/Exceptions/index.html @@ -0,0 +1,160 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Exceptions + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 88.00% covered (warning) +
+
+
88.00%
44 / 50
+
+ 62.50% covered (warning) +
+
+
62.50%
5 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
Api
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbortRowImportException.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Handler.php
+
+ 90.00% covered (success) +
+
+
90.00%
36 / 40
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/tests/coverage/Http/Controllers/AnswerController.php.html b/tests/coverage/Http/Controllers/AnswerController.php.html new file mode 100644 index 0000000..fe234a3 --- /dev/null +++ b/tests/coverage/Http/Controllers/AnswerController.php.html @@ -0,0 +1,186 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/AnswerController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AnswerController
n/a
0 / 0
n/a
0 / 0
4
n/a
0 / 0
 indexQuery
n/a
0 / 0
2
n/a
0 / 0
 index
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Http\Controllers\Controller;
6use App\Models\Answer;
7use Illuminate\Database\Eloquent\Builder;
8use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
9use Kwaadpepper\CrudPolicies\Traits\CrudController;
10
11/**
12 * @codeCoverageIgnore
13 */
14class AnswerController extends Controller
15{
16    use CrudController {
17        index as crudIndex;
18    }
19
20    /** @var Log */
21    public static $modelClass = Answer::class;
22
23    /**
24     * The Bo app layout
25     *
26     * @var string
27     */
28    public static $layout = 'layouts.app';
29
30    /**
31     * Event index crud-policies
32     *
33     * @param Builder $query
34     * @return Builder
35     */
36    public function indexQuery(Builder $query): Builder
37    {
38        $parent = request()->question;
39        if ($parent) {
40            $query->whereHas('question', function ($q) use ($parent) {
41                return $q->whereId($parent->id);
42            });
43        }
44        return $query;
45    }
46
47    /**
48     * Overload crud-policies index
49     *
50     * @param CrudIndexRequest $request
51     * @return \Illuminate\Http\Response
52     */
53    public function index(CrudIndexRequest $request)
54    {
55        /** @var \Illuminate\View\View */
56        $view = $this->crudIndex($request);
57        if ($request->ajax()) {
58            return $view;
59        }
60        return view('answer.index', $view->getData());
61    }
62}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V1/ArcadiaController.php.html b/tests/coverage/Http/Controllers/Api/V1/ArcadiaController.php.html new file mode 100644 index 0000000..e4963ea --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V1/ArcadiaController.php.html @@ -0,0 +1,511 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api/V1/ArcadiaController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ArcadiaController
n/a
0 / 0
n/a
0 / 0
57
n/a
0 / 0
 outgoingMail
n/a
0 / 0
10
n/a
0 / 0
 checkShippingRequiredFields
n/a
0 / 0
4
n/a
0 / 0
 checkRequiredFields
n/a
0 / 0
21
n/a
0 / 0
 makeXml
n/a
0 / 0
22
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V1;
4
5use App\Http\Controllers\Controller;
6use App\Http\Resources\StatusResource;
7use App\Mail\CourrierArcadiaError;
8use App\Models\Customer;
9use DOMDocument;
10use Illuminate\Http\Request;
11use Illuminate\Support\Facades\Log;
12use Illuminate\Support\Facades\Mail;
13use Illuminate\Support\Facades\Storage;
14use Illuminate\Support\Facades\Validator;
15
16/**
17 * @codeCoverageIgnore
18 */
19class ArcadiaController extends Controller
20{
21    /**
22     * @param Request $request
23     * @return StatusResource
24     */
25    public function outgoingMail(Request $request)
26    {
27        $validator = Validator::make($request->all(), [
28            'crm_id' => 'required',
29            'fields' => 'required|json',
30            'type' => 'required',
31            'shipping' => 'required_if:type,default|json'
32        ]);
33
34        if ($validator->fails()) {
35            return new StatusResource([
36                'status' => 'KO',
37                'code' => 201,
38                'message' => 'Error on required fields.',
39                'errors' => $validator->errors()
40            ]);
41        }
42
43        $brandId  = $request->brand_id;
44        $crmId    = $request->crm_id;
45        $fields   = json_decode($request->fields);
46        $type     = $request->type;
47        $shipping = $type === 'default' ? json_decode($request->shipping) : false;
48
49        $customer = Customer::where('id', $crmId)->first();
50        if (!$customer) {
51            return new StatusResource(['status' => 'KO', 'code' => 202, 'message' => 'User not found.']);
52        }
53
54        $CustomerInfo = $customer->infos()->where('brand_id', $brandId)->first();
55
56        if (!$CustomerInfo) {
57            return new StatusResource(['status' => 'KO', 'code' => 202, 'message' => 'User not found.']);
58        }
59
60        $check = $this->checkRequiredFields($fields, $type);
61        if ($check['status'] !== 'OK') {
62            return new StatusResource($check);
63        }
64
65        if ($type === 'default') {
66            $check = $this->checkShippingRequiredFields($shipping);
67            if ($check['status'] !== 'OK') {
68                return new StatusResource($check);
69            }
70        }
71
72        $xml = $this->makeXml($fields, $type, $customer, $CustomerInfo, $shipping);
73
74        try {
75            Storage::disk('arcadia')->put(date("Ymd_His") . microtime(true) . ".xml", $xml);
76            return new StatusResource(['status' => 'OK']);
77        } catch (\Exception $e) {
78            Log::critical("ARCADIA XML CREATION FAILED : " . $e->getMessage());
79            Mail::to(config('mail.MAIL_ADMIN'))->send(new CourrierArcadiaError(
80                "ARCADIA XML CREATION FAILED",
81                "Une erreur est survenue lors de la création du fichier XML : " . $e->getMessage(),
82                $xml ?: ''
83            ));
84            return new StatusResource(['status' => 'KO', 'code' => 204, 'message' => 'Arcadia XML Creation failed.']);
85        }
86    }
87
88    /**
89     * @param mixed $fields
90     * @return array
91     */
92    private function checkShippingRequiredFields($fields)
93    {
94        $test = [
95            'firstname',
96            'lastname',
97            'address',
98            'city',
99            'postcode'
100        ];
101
102        foreach ($test as $t) {
103            if (!array_key_exists($t, $fields)) {
104                $errors[] = ['field' => $t, 'error' => 'required'];
105            }
106        }
107
108        if (!empty($errors)) {
109            return ['status' => 'KO', 'code' => 203, 'message' => $errors];
110        }
111
112        return ['status' => 'OK'];
113    }
114
115    /**
116     *
117     * @param mixed $fields
118     * @param mixed $type
119     * @return array
120     */
121    // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
122    private function checkRequiredFields($fields, $type): array
123    {
124        $errors = [];
125
126        $key = array_search('operation', array_column($fields->root, 'fieldName'));
127        if ($key === false) {
128            $errors[] = ['field' => 'operation', 'error' => 'required'];
129        }
130
131        $key = array_search('support_code', array_column($fields->orderLine, 'fieldName'));
132        if ($key === false) {
133            $errors[] = ['field' => 'support_code', 'error' => 'required'];
134        }
135
136        $key = array_search('label_type_support', array_column($fields->orderLine, 'fieldName'));
137        if ($key === false) {
138            $errors[] = ['field' => 'label_type_support', 'error' => 'required'];
139        }
140
141        $key = array_search('order_date', array_column($fields->root, 'fieldName'));
142        if ($key === false) {
143            $errors[] = ['field' => 'order_date', 'error' => 'required'];
144        }
145
146        $regex = join('', [
147            '/^(((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((19|[2-9]\d)\d{2}))|',
148            '((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((19|[2-9]\d)\d{2}))',
149            '|((0[1-9]|1\d|2[0-8])\/02\/((19|[2-9]\d)\d{2}))|(29\/02\/((1[6-9]|[2-9]\d)(0[48]',
150            '|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/'
151        ]);
152        if (!preg_match($regex, $fields->root[$key]->fieldValue)) {
153            $errors[] = ['field' => 'order_date', 'error' => 'format must be DD/MM/YYYY'];
154        }
155
156        if ($type === 'default') {
157            $key = array_search('order_num', array_column($fields->root, 'fieldName'));
158            if ($key === false) {
159                $errors[] = ['field' => 'order_num', 'error' => 'required for default type'];
160            }
161
162            if (!is_numeric($fields->root[$key]->fieldValue) || strlen($fields->root[$key]->fieldValue) !== 9) {
163                $errors[] = ['field' => 'order_num', 'error' => 'format must be 9 numeric characters'];
164            }
165
166            $key = array_search('state_order_cb', array_column($fields->root, 'fieldName'));
167            if ($key === false) {
168                $errors[] = ['field' => 'state_order_cb', 'error' => 'required for default type'];
169            }
170
171            if ($fields->root[$key]->fieldValue !== 1 && $fields->root[$key]->fieldValue !== 'Null') {
172                $errors[] = ['field' => 'state_order_cb', 'error' => 'value must be 1 or Null (Yes Null not NULL)'];
173            }
174
175            $orderCB = $fields->root[$key]->fieldValue;
176
177            $key = array_search('state_order_check', array_column($fields->root, 'fieldName'));
178            if ($key === false) {
179                $errors[] = ['field' => 'state_order_check', 'error' => 'required for default type'];
180            }
181
182            if ($fields->root[$key]->fieldValue !== 1 && $fields->root[$key]->fieldValue !== 'Null') {
183                $errors[] = ['field' => 'state_order_check', 'error' => 'value must be 1 or Null (Yes Null not NULL)'];
184            }
185
186            $orderCheck = $fields->root[$key]->fieldValue;
187
188            if ($orderCB === $orderCheck) {
189                $errors[] = [
190                    'field' => 'state_order',
191                    'error' => 'state_order_cb and state_order_check values must be different'
192                ];
193            }
194
195            $key = array_search('package_unit_weight', array_column($fields->orderLine, 'fieldName'));
196            if ($key === false) {
197                $errors[] = ['field' => 'package_unit_weight', 'error' => 'required for default type'];
198            }
199        } elseif ($type === 'game') {
200            $key = array_search('code_unique', array_column($fields->orderLine, 'fieldName'));
201            if ($key === false) {
202                $errors[] = ['field' => 'code_unique', 'error' => 'required for game type'];
203            }
204        }//end if
205
206        if (!empty($errors)) {
207            return ['status' => 'KO', 'code' => 203, 'message' => $errors];
208        }
209
210        return ['status' => 'OK'];
211    }
212
213    /**
214     * @param mixed $fields
215     * @param mixed $type
216     * @param mixed $customer
217     * @param mixed $customerInfos
218     * @param mixed $shipping
219     * @return string|false
220     */
221    // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
222    private function makeXml($fields, $type, $customer, $customerInfos, $shipping)
223    {
224        $pivot = [
225            'order_num' => 'Num_Commande',
226            // Required if type = default - format 9 numeric characters.
227            'order_date' => 'Date_Commande',
228            // Required - format : DD/MM/YYYY .
229            'operation' => 'Operation',
230            // Required.
231            'support_code' => 'Code_Support',
232            // Required.
233            'label_type_support' => 'Libelle_Type_Support',
234            // Required.
235            'package_unit_weight' => 'Poids_Unitaire_Colis',
236            // Required if type = default.
237            'qty' => 'Quantite',
238            'code_on_pack' => 'Code_on_Pack',
239            'code_unique' => 'Code_Unique',
240            // Required if type = game.
241            // Required if type = default - format 1 ou Null (oui Null et pas NULL).
242            'state_order_cb' => 'Etat_Commande_CB',
243            // Required if type = default - format 1 ou Null (oui Null et pas NULL).
244            'state_order_check' => 'Etat_Commande_Cheque',
245
246        ];
247
248        $dom               = new DOMDocument('1.0', 'UTF-8');
249        $dom->formatOutput = true;
250
251        $root = $dom->createElement('export_arcadia');
252        $dom->appendChild($root);
253        $order = $root->appendChild($dom->createElement('Commande'));
254
255        $key = array_search('order_num', array_column($fields->root, 'fieldName'));
256        if ($key === false) {
257            $order->appendChild($dom->createElement('Num_Commande'));
258        } else {
259            $order->appendChild($dom->createElement('Num_Commande', $fields->root[$key]->fieldValue));
260        }
261
262        foreach ($fields->root as $value) {
263            // phpcs:ignore PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace
264            if (
265                isset($pivot[$value->fieldName]) && $value->fieldName !== 'state_order_cb' &&
266                $value->fieldName !== 'state_order_check' && $value->fieldName !== 'order_num'
267            ) {
268                $order->appendChild($dom->createElement($pivot[$value->fieldName], $value->fieldValue));
269            }
270        }
271
272        if ($type === 'default') {
273            $key = array_search('state_order_cb', array_column($fields->root, 'fieldName'));
274            if ($key) {
275                $order->appendChild($dom->createElement('Etat_Commande_CB', $fields->root[$key]->fieldValue));
276            }
277
278            $key = array_search('state_order_check', array_column($fields->root, 'fieldName'));
279            if ($key) {
280                $order->appendChild($dom->createElement('Etat_Commande_Cheque', $fields->root[$key]->fieldValue));
281            }
282        }
283
284        $order->appendChild($dom->createElement('Email_conso'))
285        ->appendChild($dom->createCDATASection("$customer->email"));
286        $order->appendChild($dom->createElement('Civilite'));
287
288        if ($type === 'default') {
289            $order->appendChild($dom->createElement('Nom'))
290                ->appendChild($dom->createCDATASection("$shipping->lastname"));
291            $order->appendChild($dom->createElement('Prenom'))
292                ->appendChild($dom->createCDATASection("$shipping->firstname"));
293            $order->appendChild($dom->createElement('Adresse'))
294                ->appendChild($dom->createCDATASection("$shipping->address"));
295            $order->appendChild($dom->createElement('Adresse_Complementaire'));
296            $order->appendChild($dom->createElement('CP'))
297            ->appendChild($dom->createCDATASection("$shipping->postcode"));
298            $order->appendChild($dom->createElement('Ville'))->appendChild($dom->createCDATASection("$shipping->city"));
299            if (isset($shipping->phone) && !empty($shipping->phone)) {
300                $order->appendChild($dom->createElement('Telephone'))
301                    ->appendChild($dom->createCDATASection("$shipping->phone"));
302            } else {
303                $order->appendChild($dom->createElement('Telephone'));
304            }
305        } else {
306            $order->appendChild($dom->createElement('Nom'))
307                ->appendChild($dom->createCDATASection("$customerInfos->lastname"));
308            $order->appendChild($dom->createElement('Prenom'))
309                ->appendChild($dom->createCDATASection("$customerInfos->firstname"));
310            $order->appendChild($dom->createElement('Adresse'))
311                ->appendChild($dom->createCDATASection("$customerInfos->address"));
312            $order->appendChild($dom->createElement('Adresse_Complementaire'))
313                ->appendChild($dom->createCDATASection("$customerInfos->address2"));
314            $order->appendChild($dom->createElement('CP'))
315                ->appendChild($dom->createCDATASection("$customerInfos->postcode"));
316            $order->appendChild($dom->createElement('Ville'))
317                ->appendChild($dom->createCDATASection("$customerInfos->city"));
318            $order->appendChild($dom->createElement('Telephone'))
319                ->appendChild($dom->createCDATASection("$customerInfos->phone"));
320        }//end if
321
322        $orderLine = $order->appendChild($dom->createElement('Ligne_de_Commande'));
323
324        $key = array_search('qty', array_column($fields->orderLine, 'fieldName'));
325        if ($key === false) {
326            $orderLine->appendChild($dom->createElement('Quantite', '1'));
327        }
328
329        $key = array_search('package_unit_weight', array_column($fields->orderLine, 'fieldName'));
330        if ($key === false) {
331            $orderLine->appendChild($dom->createElement('Poids_Unitaire_Colis'));
332        }
333
334        foreach ($fields->orderLine as $value) {
335            // phpcs:ignore PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace
336            if (
337                isset($pivot[$value->fieldName]) &&
338                $value->fieldName !== 'code_on_pack' &&
339                $value->fieldName !== 'code_unique'
340            ) {
341                $orderLine->appendChild($dom->createElement($pivot[$value->fieldName], $value->fieldValue));
342            }
343        }
344
345        if ($type === 'game') {
346            $key = array_search('code_on_pack', array_column($fields->orderLine, 'fieldName'));
347            if ($key) {
348                $orderLine->appendChild(
349                    $dom->createElement('Code_on_Pack')
350                )->appendChild($dom->createCDATASection($fields->orderLine[$key]->fieldValue));
351            }
352
353            $key = array_search('code_unique', array_column($fields->orderLine, 'fieldName'));
354            if ($key) {
355                $orderLine->appendChild(
356                    $dom->createElement('Code_Unique')
357                )->appendChild($dom->createCDATASection($fields->orderLine[$key]->fieldValue));
358            }
359        }
360
361        return $dom->saveXML();
362    }
363}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V1/CustomerController.php.html b/tests/coverage/Http/Controllers/Api/V1/CustomerController.php.html new file mode 100644 index 0000000..bcc3691 --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V1/CustomerController.php.html @@ -0,0 +1,784 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api/V1/CustomerController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerController
n/a
0 / 0
n/a
0 / 0
69
n/a
0 / 0
 store
n/a
0 / 0
12
n/a
0 / 0
 show
n/a
0 / 0
6
n/a
0 / 0
 update
n/a
0 / 0
22
n/a
0 / 0
 authenticate
n/a
0 / 0
6
n/a
0 / 0
 changePassword
n/a
0 / 0
5
n/a
0 / 0
 activateAccount
n/a
0 / 0
4
n/a
0 / 0
 delete
n/a
0 / 0
4
n/a
0 / 0
 toggleOptin
n/a
0 / 0
4
n/a
0 / 0
 removeOptin
n/a
0 / 0
2
n/a
0 / 0
 addSingleOptin
n/a
0 / 0
4
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V1;
4
5use App\Enums\Country;
6use App\Http\Controllers\Controller;
7use App\Http\Resources\CustomerResource;
8use App\Http\Resources\StatusResource;
9use App\Models\Customer;
10use App\Models\CustomerInfo;
11use App\Models\CustomerLogin;
12use App\Models\CustomerOptInHistory;
13use App\Models\SingleOptin;
14use Illuminate\Http\Request;
15use Illuminate\Support\Facades\Hash;
16use Illuminate\Support\Facades\Validator;
17use Illuminate\Support\Str;
18
19/**
20 * @codeCoverageIgnore
21 */
22class CustomerController extends Controller
23{
24    /**
25     * Store a newly created resource in storage
26     * @param Request $request
27     * @return CustomerResource|StatusResource
28     */
29    // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
30    public function store(Request $request)
31    {
32        $validator = Validator::make($request->all(), [
33            'email' => 'required|email',
34            'password' => 'required',
35            'firstname' => 'required',
36            'lastname' => 'required',
37            'opt_in' => 'required',
38            'birth_date' => 'date_format:Y-m-d'
39        ]);
40
41        if ($validator->fails()) {
42            return new StatusResource([
43                'status' => 'KO', 'code' => 102,
44                'message' => 'Error on required fields.', 'errors' => $validator->errors()
45            ]);
46        }
47
48        $brandId   = $request->brand_id;
49        $email     = strtolower(filter_var($request->email, FILTER_SANITIZE_EMAIL));
50        $firstname = filter_var($request->firstname, FILTER_SANITIZE_STRING);
51        $lastname  = filter_var($request->lastname, FILTER_SANITIZE_STRING);
52        $optIn     = filter_var($request->opt_in, FILTER_SANITIZE_STRING);
53        $hashcode  = Str::uuid();
54
55        // Récupère le customer s'il existe déjà.
56        $customer = Customer::where('email', $email)->first();
57
58        // Si le customer n'existe pas déjà.
59        if (!$customer) {
60            $customer        = new Customer();
61            $customer->email = $email;
62            $customer->save();
63            $singleOptin = SingleOptin::where([['email', $email], ['brand_id', $brandId]])->first();
64            if ($singleOptin) {
65                $singleOptin->delete();
66            }
67        } else {
68            // Récupère les infos s'ils existent déjà.
69            $CustomerInfo = CustomerInfo::where([['customer_id', $customer->id], ['brand_id', $brandId]])->first();
70            if ($CustomerInfo) {
71                return new StatusResource([
72                    'status' => 'KO', 'code' => 103,
73                    'message' => 'User already registered on this site.'
74                ]);
75            }
76        }
77
78        $customerInfos             = new CustomerInfo();
79        $customerInfos->firstname  = ucfirst(strtolower($firstname));
80        $customerInfos->lastname   = ucfirst(strtolower($lastname));
81        $customerInfos->birth_date = !empty($request->birth_date) ?
82        filter_var($request->birth_date, FILTER_SANITIZE_STRING) : null;
83        $customerInfos->address    = !empty($request->address) ?
84        filter_var($request->address, FILTER_SANITIZE_STRING) : null;
85        $customerInfos->address2   = !empty($request->address2) ?
86        filter_var($request->address2, FILTER_SANITIZE_STRING) : null;
87        $customerInfos->phone      = !empty($request->phone) ?
88        filter_var($request->phone, FILTER_SANITIZE_STRING) : null;
89        try {
90            $customerInfos->country = Country::make(filter_var($request->country, FILTER_SANITIZE_STRING));
91        } catch (\Exception $e) {
92            $customerInfos->country = Country::france();
93        }
94        $customerInfos->city        = !empty($request->city) ?
95        filter_var($request->city, FILTER_SANITIZE_STRING) : null;
96        $customerInfos->postcode    = !empty($request->postcode) ?
97        filter_var($request->postcode, FILTER_SANITIZE_STRING) : null;
98        $customerInfos->opt_in      = $optIn;
99        $customerInfos->hashcode    = $hashcode;
100        $customerInfos->brand_id    = $brandId;
101        $customerInfos->customer_id = $customer->id;
102
103        $customer->infos()->save($customerInfos);
104
105        // Création du login pour cet utilisateur sur ce service.
106        $customerLogin                  = new CustomerLogin();
107        $customerLogin->customer_id     = $customer->id;
108        $customerLogin->password        = Hash::make(trim($request->password));
109        $customerLogin->last_connection = now();
110        $customerLogin->brand_id        = $brandId;
111        $customerLogin->status          = 0;
112
113        $customer->logins()->save($customerLogin);
114
115        // Ajout dans la table d'historique des opt-in de la nouvelle valeur.
116        $optInHistory              = new CustomerOptInHistory();
117        $optInHistory->customer_id = $customer->id;
118        $optInHistory->brand_id    = $brandId;
119        $optInHistory->new_value   = $optIn;
120        $optInHistory->save();
121
122        return new CustomerResource($customer, $brandId);
123    }
124
125    /**
126     * Display the specified resource.
127     *
128     * @param  Illuminate\Http\Request $request
129     * @param  mixed                   $type
130     * @param  mixed                   $value
131     * @return CustomerResource | StatusResource
132     */
133    public function show(Request $request, $type, $value)
134    {
135        $brandId = $request->brand_id;
136
137        if ($type === 'id') {
138            $customer = Customer::find($value);
139            if (!$customer) {
140                return new StatusResource(['status' => 'KO', 'code' => 101, 'message' => 'User not found.']);
141            }
142        } elseif ($type === 'email') {
143            $customer = Customer::where('email', $value)->first();
144            if (!$customer) {
145                return new StatusResource(['status' => 'KO', 'code' => 101, 'message' => 'User not found.']);
146            }
147
148            $CustomerInfo = $customer->infos()->where('brand_id', $brandId)->first();
149
150            if (!$CustomerInfo) {
151                return new StatusResource(['status' => 'KO', 'code' => 101, 'message' => 'User not found.']);
152            }
153        } else {
154            return new StatusResource(['status' => 'KO', 'code' => 101, 'message' => 'User not found.']);
155        }
156
157        return new CustomerResource($customer, $brandId);
158    }
159
160    /**
161     * Update the specified resource in storage.
162     *
163     * @param  \Illuminate\Http\Request  $request
164     * @param  int  $id
165     * @return CustomerResource|StatusResource
166     */
167    // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded, Squiz.Commenting.FunctionComment.Missing
168    public function update(Request $request, $id)
169    {
170        $validator = Validator::make($request->all(), [
171            'birth_date' => 'date_format:Y-m-d'
172        ]);
173
174        if ($validator->fails()) {
175            return new StatusResource([
176                'status' => 'KO', 'code' => 104,
177                'message' => 'Error on required fields.', 'errors' => $validator->errors()
178            ]);
179        }
180
181        $brandId  = $request->brand_id;
182        $customer = Customer::find($id);
183
184        if (!$customer) {
185            return new StatusResource(['status' => 'KO', 'code' => 105, 'message' => 'User not found.']);
186        }
187
188        $CustomerInfo = $customer->infos()->where('brand_id', $brandId)->first();
189        if (!$CustomerInfo) {
190            return new StatusResource(['status' => 'KO', 'code' => 105, 'message' => 'User not found.']);
191        }
192
193        if ($request->has('firstname')) {
194            $CustomerInfo->firstname = filter_var($request->firstname, FILTER_SANITIZE_STRING);
195        }
196
197        if ($request->has('lastname')) {
198            $CustomerInfo->lastname = filter_var($request->lastname, FILTER_SANITIZE_STRING);
199        }
200
201        if ($request->has('birth_date')) {
202            if (!empty($request->birth_date)) {
203                $CustomerInfo->birth_date = filter_var($request->birth_date, FILTER_SANITIZE_STRING);
204            } else {
205                $CustomerInfo->birth_date = null;
206            }
207        }
208
209        if ($request->has('address')) {
210            if (!empty($request->address)) {
211                $CustomerInfo->address = filter_var($request->address, FILTER_SANITIZE_STRING);
212            } else {
213                $CustomerInfo->address = null;
214            }
215        }
216
217        if ($request->has('address2')) {
218            if (!empty($request->address2)) {
219                $CustomerInfo->address2 = filter_var($request->address2, FILTER_SANITIZE_STRING);
220            } else {
221                $CustomerInfo->address2 = null;
222            }
223        }
224
225        if ($request->has('phone')) {
226            if (!empty($request->phone)) {
227                $CustomerInfo->phone = filter_var($request->phone, FILTER_SANITIZE_STRING);
228            } else {
229                $CustomerInfo->phone = null;
230            }
231        }
232
233        if ($request->has('country')) {
234            try {
235                $CustomerInfo->country = Country::make(filter_var($request->country, FILTER_SANITIZE_STRING));
236            } catch (\Exception $e) {
237                $CustomerInfo->country = Country::france();
238            }
239        }
240
241        if ($request->has('city')) {
242            if (!empty($request->city)) {
243                $CustomerInfo->city = filter_var($request->city, FILTER_SANITIZE_STRING);
244            } else {
245                $CustomerInfo->city = null;
246            }
247        }
248
249        if ($request->has('postcode')) {
250            if (!empty($request->postcode)) {
251                $CustomerInfo->postcode = filter_var($request->postcode, FILTER_SANITIZE_STRING);
252            } else {
253                $CustomerInfo->postcode = null;
254            }
255        }
256
257        if ($request->has('opt_in')) {
258            $optIn = filter_var($request->opt_in, FILTER_SANITIZE_STRING);
259
260            if ($CustomerInfo->opt_in != $optIn) {
261                // Ajout dans la table d'historique des opt-in de la nouvelle valeur.
262                $optInHistory              = new CustomerOptInHistory();
263                $optInHistory->customer_id = $customer->id;
264                $optInHistory->brand_id    = $brandId;
265                $optInHistory->new_value   = $optIn;
266                $optInHistory->save();
267            }
268
269            $CustomerInfo->opt_in = $optIn;
270        }
271
272        $CustomerInfo->save();
273
274        return new CustomerResource($customer, $brandId);
275    }
276
277    /**
278     * Authenticate the user
279     * @param Request $request
280     * @return CustomerResource|StatusResource
281     */
282    public function authenticate(Request $request)
283    {
284        $validator = Validator::make($request->all(), [
285            'email' => 'required|email',
286            'password' => 'required',
287        ]);
288
289        if ($validator->fails()) {
290            return new StatusResource([
291                'status' => 'KO', 'code' => 115,
292                'message' => 'Error on required fields.', 'errors' => $validator->errors()
293            ]);
294        }
295
296        $brandId  = $request->brand_id;
297        $email    = filter_var($request->email, FILTER_SANITIZE_EMAIL);
298        $password = trim($request->password);
299        $customer = Customer::where('email', $email)->first();
300        if ($customer) {
301            $login = $customer->logins()->where('brand_id', $brandId)->first();
302            if ($login) {
303                if (Hash::check($password, $login->password)) {
304                    if ($login->status === false) {
305                        $infos = $customer->infos()->where('brand_id', $brandId)->first();
306                        // "Le compte de l'utilisateur n'est pas activé"
307                        return new StatusResource(
308                            [
309                                'status' => 'KO',
310                                'code' => 116,
311                                'message' => 'Account not activated.',
312                                'user' => [
313                                    'crm_id' => $customer->id,
314                                    'firstname' => $infos->firstname,
315                                    'lastname' => $infos->lastname,
316                                    'email' => $customer->email,
317                                    'birth_date' => $infos->birth_date,
318                                    'address' => $infos->address,
319                                    'address2' => $infos->address2,
320                                    'phone' => $infos->phone,
321                                    'country' => $infos->country->label,
322                                    'city' => $infos->city,
323                                    'postcode' => $infos->postcode,
324                                    'opt_in' => $infos->opt_in,
325                                    'hashcode' => $infos->hashcode,
326                                    'status' => $login->status,
327                                ]
328                            ]
329                        );
330                    }//end if
331
332                    $login->last_connection = now();
333                    $customer->logins()->save($login);
334                    return new CustomerResource($customer, $brandId);
335                }//end if
336                // "Mot de passe incorrect"
337                return new StatusResource(['status' => 'KO', 'code' => 117, 'message' => 'Authentication failed.']);
338            } else {
339                // "Ce compte n'est pas enregistré pour cette marque"
340                return new StatusResource(['status' => 'KO', 'code' => 118, 'message' => 'User not found.']);
341            }//end if
342        } else {
343            // "Ce compte n'existe pas dans la CRM"
344            return new StatusResource(['status' => 'KO', 'code' => 119, 'message' => 'User not found.']);
345        }//end if
346    }
347
348    /**
349     * Change the password of the user for the brand
350     * @param Request $request
351     * @param mixed   $id
352     * @return StatusResource
353     */
354    public function changePassword(Request $request, $id)
355    {
356        $validator = Validator::make($request->all(), [
357            'password' => 'required',
358        ]);
359
360        if ($validator->fails()) {
361            return new StatusResource([
362                'status' => 'KO', 'code' => 107,
363                'message' => 'Error on required fields.', 'errors' => $validator->errors()
364            ]);
365        }
366
367        $brandId  = $request->brand_id;
368        $customer = Customer::find($id);
369        $password = trim($request->password);
370
371        if ($customer) {
372            $customerLogin = $customer->logins()->where('brand_id', $brandId)->first();
373            if ($customerLogin) {
374                // Check if same password.
375                if (Hash::check($password, $customerLogin->password)) {
376                    return new StatusResource(['status' => 'KO', 'code' => 123, 'message' => 'Same password.']);
377                }
378
379                $customerLogin->password = Hash::make($password);
380                $customerLogin->save();
381            } else {
382                // "Ce compte n'est pas enregistré pour cette marque"
383                return new StatusResource(['status' => 'KO', 'code' => 108, 'message' => 'User not found.']);
384            }
385        } else {
386            // "Ce compte n'existe pas"
387            return new StatusResource(['status' => 'KO', 'code' => 109, 'message' => 'User not found.']);
388        }
389        return new StatusResource(['status' => 'OK', 'message' => 'Update complete.']);
390    }
391
392    /**
393     * Activate the account of the user
394     * @param Request $request
395     * @param mixed   $id
396     * @return StatusResource
397     */
398    public function activateAccount(Request $request, $id)
399    {
400        $brandId = $request->brand_id;
401
402        $customer = Customer::find($id);
403        if ($customer) {
404            $login = $customer->logins()->where('brand_id', $brandId)->first();
405            if ($login) {
406                if ($login->status === false) {
407                    $login->status = true;
408                    $login->save();
409                    // "Le compte a bien été activé"
410                    return new StatusResource(['status' => 'OK', 'message' => 'Update complete.']);
411                } else {
412                    // "Ce compte est déjà activé"
413                    return new StatusResource(['status' => 'KO', 'code' => 110, 'message' => 'User already active.']);
414                }
415            } else {
416                // "Ce compte n'est pas enregistré pour cette marque"
417                return new StatusResource(['status' => 'KO', 'code' => 111, 'message' => 'User not found.']);
418            }
419        } else {
420            // "Cet utilisateur n'existe pas dans la CRM"
421            return new StatusResource(['status' => 'KO', 'code' => 112, 'message' => 'User not found.']);
422        }
423    }
424
425    /**
426     * Delete the user account for the brand
427     * @param Request $request
428     * @param mixed   $id
429     */
430    // phpcs:disable Squiz.PHP.NonExecutableCode.Unreachable, Squiz.Commenting.FunctionComment.Missing
431    public function delete(Request $request, $id)
432    {
433        die("Not implemented yet");
434        // TODO : Gestion avec mesBR.
435        $brandId = $request->brand_id;
436
437        $customer = Customer::find($id);
438        if ($customer) {
439            $customer->infos()->where('brand_id', $brandId)->first()->delete();
440            $customer->logins()->where('brand_id', $brandId)->first()->delete();
441
442            if (count($customer->infos) === 0 && count($customer->logins) === 0) {
443                $customer->optInHistory()->delete();
444                $customer->delete();
445                // Suppression du compte de la CRM terminée.
446                return new StatusResource(['status' => 'OK', 'message' => 'Deletion complete.']);
447            } else {
448                // Suppression des informations du compte pour la marque terminée.
449                return new StatusResource(['status' => 'OK', 'message' => 'Deletion complete.']);
450            }
451        } else {
452            return new StatusResource(['status' => 'KO', 'code' => 106, 'message' => 'User not found.']);
453        }
454        // phpcs:enable Squiz.PHP.NonExecutableCode.Unreachable, Squiz.Commenting.FunctionComment.Missing
455    }
456
457    /**
458     * Change the status of the opt-in for the customer on this brand
459     * @param Request $request
460     * @param mixed   $id
461     * @return StatusResource
462     */
463    public function toggleOptin(Request $request, $id)
464    {
465        // TODO : Liaison des opt-in avec MailJet.
466        $brandId = $request->brand_id;
467
468        $customer = Customer::find($id);
469        if ($customer) {
470            $infos = $customer->infos()->where('brand_id', $brandId)->first();
471            if ($infos) {
472                $infos->opt_in = $infos->opt_in === 0 ? 1 : 0;
473
474                // On enregistre l'historique du changement d'optin.
475                $optinHistory              = new CustomerOptInHistory();
476                $optinHistory->customer_id = $customer->id;
477                $optinHistory->new_value   = $infos->opt_in;
478                $optinHistory->brand_id    = $brandId;
479                $optinHistory->save();
480
481                $infos->save();
482                // "Changement effectué"
483                return new StatusResource(['status' => 'OK', 'message' => 'Update complete.']);
484            } else {
485                // "Ce compte n'a pas d'information liés à cette marque"
486                return new StatusResource(['status' => 'KO', 'code' => 113, 'message' => 'User not found.']);
487            }
488        } else {
489            // "Cet utilisateur n'existe pas dans la CRM"
490            return new StatusResource(['status' => 'KO', 'code' => 114, 'message' => 'User not found.']);
491        }//end if
492    }
493
494    /**
495     * Remove opt-in for the customer on this brand
496     * @param Request $request
497     * @param mixed   $hashcode
498     * @return StatusResource
499     */
500    public function removeOptin(Request $request, $hashcode)
501    {
502        // TODO : Liaison des opt-in avec MailJet.
503        $brandId = $request->brand_id;
504
505        $infos = CustomerInfo::where([
506            ['brand_id', $brandId],
507            ['hashcode', $hashcode]
508        ])->first();
509
510        if ($infos) {
511            $infos->opt_in = 0;
512
513            // On enregistre l'historique du changement d'optin.
514            $optinHistory              = new CustomerOptInHistory();
515            $optinHistory->customer_id = $infos->customer_id;
516            $optinHistory->new_value   = 0;
517            $optinHistory->brand_id    = $brandId;
518            $optinHistory->save();
519
520            $infos->save();
521            // "Changement effectué"
522            return new StatusResource(['status' => 'OK', 'message' => 'Update complete.']);
523        } else {
524            // "Ce compte n'a pas d'information liés à cette marque"
525            return new StatusResource(['status' => 'KO', 'code' => 122, 'message' => 'User not found.']);
526        }
527    }
528
529    /**
530     * @param Request $request
531     * @return void
532     */
533    // phpcs:ignore Squiz.PHP.NonExecutableCode.Unreachable, Squiz.Commenting.FunctionComment.Missing
534    public function addSingleOptin(Request $request)
535    {
536        $validator = Validator::make($request->all(), [
537            'email' => 'required|email',
538        ]);
539
540        if ($validator->fails()) {
541            return new StatusResource([
542                'status' => 'KO', 'code' => 120,
543                'message' => 'Error on required fields.', 'errors' => $validator->errors()
544            ]);
545        }
546
547        $brandId = $request->brand_id;
548        $email   = filter_var($request->email, FILTER_SANITIZE_EMAIL);
549
550        // Check si le customer ou l'optin simple existe avant de l'ajouter en optin simple.
551        if (
552            !SingleOptin::where([['email', $email], ['brand_id', $brandId]])->exists() &&
553            !CustomerInfo::join("customers", "customer_infos.customer_id", "=", "customers.id")->where([
554                ['brand_id', $brandId], ['email', $email]
555            ])->exists()
556        ) {
557            $singleOptin           = new SingleOptin();
558            $singleOptin->email    = $email;
559            $singleOptin->brand_id = $brandId;
560            $singleOptin->save();
561        } else {
562            // "Cet utilisateur est déjà inscrit à la newsletter pour cette marque"
563            return new StatusResource([
564                'status' => 'KO', 'code' => 121,
565                'message' => 'This user is already subscribed to the newsletter for this brand.'
566            ]);
567        }
568        return new StatusResource(['status' => 'OK', 'message' => 'Optin added.']);
569    }
570}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V1/dashboard.html b/tests/coverage/Http/Controllers/Api/V1/dashboard.html new file mode 100644 index 0000000..a549972 --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V1/dashboard.html @@ -0,0 +1,287 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Controllers/Api/V1 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V1/index.html b/tests/coverage/Http/Controllers/Api/V1/index.html new file mode 100644 index 0000000..3f3dfde --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V1/index.html @@ -0,0 +1,105 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api/V1 + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ArcadiaController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
CustomerController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Http/Controllers/Api/V2/AnswerController.php.html b/tests/coverage/Http/Controllers/Api/V2/AnswerController.php.html new file mode 100644 index 0000000..60f21cd --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V2/AnswerController.php.html @@ -0,0 +1,190 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api/V2/AnswerController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
AnswerController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
 storeCustomerAnswers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Http\Controllers\Controller;
6use App\Http\Requests\Api\V2\StoreCustomerAnswers;
7use App\Models\Answer;
8use App\Models\Customer;
9use App\Models\Service;
10use Illuminate\Http\JsonResponse;
11
12class AnswerController extends Controller
13{
14    /**
15     * Store the customer answers
16     *
17     * @param  StoreCustomerAnswers $request
18     * @param  Service              $service
19     * @param  Customer             $customer
20     * @return JsonResponse
21     */
22    public function storeCustomerAnswers(
23        StoreCustomerAnswers $request,
24        Service $service,
25        Customer $customer
26    ): JsonResponse {
27        // * Make sure we are accessing a customer that belongs to our service.
28        $this->authorize('customerBelongsToService', $customer);
29
30        $existingAnswerIds = $customer->getAnswers($service)->pluck('id');
31        $answersID         = Answer::whereIn('id', $request->answers)->pluck('id');
32        $customer->answers()->sync($answersID->merge($existingAnswerIds));
33        return response()->json();
34    }
35}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V2/ApiController.php.html b/tests/coverage/Http/Controllers/Api/V2/ApiController.php.html new file mode 100644 index 0000000..967ed30 --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V2/ApiController.php.html @@ -0,0 +1,174 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api/V2/ApiController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 ping
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Http\Controllers\Controller;
6use Illuminate\Http\JsonResponse;
7
8class ApiController extends Controller
9{
10    /**
11     * Just returns HTTP 200 response
12     *
13     * @return JsonResponse
14     */
15    public function ping(): JsonResponse
16    {
17        return response()->json();
18    }
19}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V2/ArcadiaController.php.html b/tests/coverage/Http/Controllers/Api/V2/ArcadiaController.php.html new file mode 100644 index 0000000..d31b74d --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V2/ArcadiaController.php.html @@ -0,0 +1,357 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api/V2/ArcadiaController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
31 / 31
ArcadiaController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
8
+
+ 100.00% covered (success) +
+
+
100.00%
31 / 31
 outgoingMailDefault
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 outgoingMailGame
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 handleArcadiaMail
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
21 / 21
 makeXml
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Enums\ArcadiaMailType;
6use App\Enums\ArcadiaOrderType;
7use App\Http\Controllers\Controller;
8use App\Http\Requests\Api\V2\BaseApiRequest;
9use App\Http\Requests\Api\V2\SendArchadiaDefaultMailRequest;
10use App\Http\Requests\Api\V2\SendArchadiaGameMailRequest;
11use App\Mail\CourrierArcadiaError;
12use DOMDocument;
13use Illuminate\Http\JsonResponse;
14use Illuminate\Support\Facades\Log;
15use Illuminate\Support\Facades\Mail;
16use Illuminate\Support\Facades\Storage;
17use Illuminate\Support\Str;
18
19/**
20 * ! Ici c'est du sale à cause d'arcadia
21 * Ils ont fait le choix de rajouter une fonctionnalité
22 * sur une methode existante en rajoutant des conditions
23 * sales. Ils ont donc une seule méthode pour envoyer plusieurs
24 * types de réclas avec des champs différents...
25 *
26 * Donc une séparation est faite sur les endpoints CRM et dans le client
27 * pour éviter une confusion de nos utilisateurs
28 */
29class ArcadiaController extends Controller
30{
31    /**
32     * Send arcadia default mail (reclamations)
33     *
34     * @param SendArchadiaDefaultMailRequest $request
35     * @return JsonResponse
36     */
37    public function outgoingMailDefault(SendArchadiaDefaultMailRequest $request): JsonResponse
38    {
39        return $this->handleArcadiaMail($request, ArcadiaMailType::default());
40    }
41
42    /**
43     * Send arcadia game mail (games)
44     *
45     * @param SendArchadiaGameMailRequest $request
46     * @return JsonResponse
47     */
48    public function outgoingMailGame(SendArchadiaGameMailRequest $request): JsonResponse
49    {
50        return $this->handleArcadiaMail($request, ArcadiaMailType::game());
51    }
52
53    /**
54     * Form xml message and save it in the apropriate storage location
55     * to be picked up by the arcadia job
56     *
57     * @param BaseApiRequest  $request
58     * @param ArcadiaMailType $type
59     * @return JsonResponse
60     * @throws \Exception If an error occurs while generating and saving the xml file.
61     */
62    private function handleArcadiaMail(BaseApiRequest $request, ArcadiaMailType $type): JsonResponse
63    {
64        $request = $request->merge([
65            'order_num' => str_pad(\intval($request->order_num), 9, '0', STR_PAD_LEFT)
66        ]);
67        $xml     = 'none';
68        try {
69            $customerInfo = \array_intersect_key($request->all()['deliver'], [
70                'email' => '',
71                'firstname' => '',
72                'lastname' => '',
73                'address' => '',
74                'address2' => '',
75                'city' => '',
76                'postcode' => '',
77                'phone' => ''
78            ]);
79            $orderType    = ArcadiaOrderType::make(intval($request->state_order));
80            $xml          = $this->makeXml($request, $type, $orderType, $customerInfo);
81
82            // * Format xml.
83            $doc                     = new DOMDocument('1.0', 'utf-8');
84            $doc->xmlStandalone      = true;
85            $doc->preserveWhiteSpace = false;
86            $doc->formatOutput       = true;
87            $doc->loadXML($xml);
88            $xml = $doc->saveXML();
89
90            Storage::disk('arcadia')->put(Str::slug(sprintf(
91                'v2_order_%s_%s_%s%s',
92                $request->order_num,
93                $type->label,
94                date('Ymd_His'),
95                \microtime(true)
96            )) . '.xml', $xml);
97            return response()->json();
98            // @codeCoverageIgnoreStart
99        } catch (\Exception $e) {
100            Log::critical("ARCADIA XML CREATION FAILED FOR $request->order_num : " . $e->getMessage());
101            if (!config('app.debug')) {
102                dispatch(function () use ($xml, $e, $request) {
103                    Mail::to(config('mail.MAIL_ADMIN'))->send(new CourrierArcadiaError(
104                        "ARCADIA XML CREATION FAILED FOR $request->order_num",
105                        "Une erreur est survenue lors de la création du fichier XML : " . $e->getMessage(),
106                        $xml ?: ''
107                    ));
108                })->afterResponse();
109            }
110            throw $e;
111            // @codeCoverageIgnoreEnd
112        }//end try
113    }
114
115    /**
116     * render Arcadia xml for order
117     *
118     * @param BaseApiRequest   $request
119     * @param ArcadiaMailType  $type
120     * @param ArcadiaOrderType $orderType
121     * @param array            $customerInfo
122     * @return string
123     */
124    private function makeXml(
125        BaseApiRequest $request,
126        ArcadiaMailType $type,
127        ArcadiaOrderType $orderType,
128        array $customerInfo
129    ): string {
130        $arcadiaMailType = $type->equals(ArcadiaMailType::default()) ?
131            'mail.arcadiaOrders.default' : 'mail.arcadiaOrders.game';
132        return view($arcadiaMailType, [
133            'request' => $request,
134            'type' => $type,
135            'orderType' => $orderType,
136            'customerInfo' => $customerInfo
137        ])->render();
138    }
139}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V2/CustomerController.php.html b/tests/coverage/Http/Controllers/Api/V2/CustomerController.php.html new file mode 100644 index 0000000..ee26ed7 --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V2/CustomerController.php.html @@ -0,0 +1,512 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api/V2/CustomerController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
77 / 77
CustomerController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
26
+
+ 100.00% covered (success) +
+
+
100.00%
77 / 77
 store
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
7
+
+ 100.00% covered (success) +
+
+
100.00%
25 / 25
 show
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 update
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
 destroy
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
6
+
+ 100.00% covered (success) +
+
+
100.00%
17 / 17
 authenticate
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
 activateAccount
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Enums\ApiError;
6use App\Exceptions\Api\V2\ApiErrorException;
7use App\Exceptions\Api\V2\CrmException;
8use App\Http\Controllers\Controller;
9use App\Http\Requests\Api\V2\AuthenticateRequest;
10use App\Http\Requests\Api\V2\StoreCustomerRequest;
11use App\Http\Requests\Api\V2\UpdateCustomerRequest;
12use App\Http\Resources\Api\V2\CustomerResource;
13use App\Jobs\UpdateCustomerLastConnection;
14use App\Models\Customer;
15use App\Models\Service;
16use App\Models\SingleOptin;
17use Illuminate\Database\Eloquent\ModelNotFoundException;
18use Illuminate\Http\JsonResponse;
19use Illuminate\Http\Resources\Json\JsonResource;
20use Illuminate\Support\Facades\DB;
21
22class CustomerController extends Controller
23{
24    /**
25     * Creates a new customer for the specified service.
26     *
27     * @param StoreCustomerRequest $request
28     * @param Service              $service
29     * @return JsonResource
30     * @throws ApiErrorException With ApiError::customerExists if the customer with this email already exists.
31     * @throws CrmException If saving the customer to DB failed.
32     */
33    public function store(StoreCustomerRequest $request, Service $service): JsonResource
34    {
35        $data = (object)$request->validated();
36
37        /** @var \App\Models\Customer */
38        $customer = Customer::where('email', $data->email)->firstOrCreate([
39            'email' => $data->email
40        ]);
41
42        DB::transaction(function () use (&$customer, $data, $request, $service) {
43
44            $saved = $customer->save();
45            $customer->setOptIn($service->brand, $data->opt_in);
46
47            // * Delete Single optin if exists.
48            /** @var \App\Models\SingleOptin|null $singleOptin */
49                $singleOptin = SingleOptin::where('email', $data->email)
50                ->where('brand_id', $service->brand->id)->first();
51            if ($singleOptin) {
52                $saved = $singleOptin->delete() && $saved;
53            }
54
55            /** @var \App\Models\CustomerInfo */
56            $customerInfo = $customer->info($service->brand, Customer::FIRST_OR_NEW);
57            if ($customerInfo->exists) {
58                throw new ApiErrorException(ApiError::customerExists());
59            }
60            $customerInfo->fill($request->validated());
61            $customerInfo->brand()->associate($service->brand);
62            $customerInfo->customer()->associate($customer);
63            $saved = $customerInfo->save() && $saved;
64
65            /** @var \App\Models\CustomerLogin */
66            $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_NEW);
67            $customerLogin->fill($request->validated());
68            $customerLogin->brand()->associate($service->brand);
69            $customerLogin->customer()->associate($customer);
70            $saved = $customerLogin->save() && $saved;
71
72            if (!$saved) {
73                // @codeCoverageIgnoreStart
74                throw new CrmException('Api failed to store customer.');
75                // @codeCoverageIgnoreEnd
76            }
77        });
78
79        return new CustomerResource($service->brand, $customer);
80    }
81
82    /**
83     * Renders the specific customer infos
84     *
85     * @param  Service  $service
86     * @param  Customer $customer
87     * @return JsonResource
88     */
89    public function show(Service $service, Customer $customer): JsonResource
90    {
91        // * Make sure we are accessing a customer that belongs to our service.
92        $this->authorize('customerBelongsToService', $customer);
93
94        return new CustomerResource($service->brand, $customer);
95    }
96
97    /**
98     * Updates information for a specific customer
99     *
100     * @param  UpdateCustomerRequest $request
101     * @param  Service               $service
102     * @param  Customer              $customer
103     * @return JsonResource
104     * @throws CrmException If saving the customer to DB failed.
105     */
106    public function update(UpdateCustomerRequest $request, Service $service, Customer $customer): JsonResource
107    {
108        // * Make sure we are accessing a customer that belongs to our service.
109        $this->authorize('customerBelongsToService', $customer);
110
111        DB::transaction(function () use ($request, $customer, $service) {
112            // To correct with create.
113            $customer->fill($request->validated());
114            $saved = $customer->save();
115            if ($request->has('opt_in')) {
116                $customer->setOptIn($service->brand, $request->opt_in);
117            }
118
119            /** @var \App\Models\CustomerInfo */
120            $customerInfo = $customer->info($service->brand, Customer::FIRST_OR_FAIL);
121            $customerInfo->fill($request->validated());
122            $saved = $customerInfo->save() && $saved;
123
124            /** @var \App\Models\CustomerLogin */
125            $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
126            $customerLogin->fill($request->validated());
127            $saved = $customerLogin->save() && $saved;
128
129            if (!$saved) {
130                // @codeCoverageIgnoreStart
131                throw new CrmException('Api failed to update customer.');
132                // @codeCoverageIgnoreEnd
133            }
134        });
135
136        return new CustomerResource($service->brand, $customer);
137    }
138
139    /**
140     * Deletes customer information for a specific service
141     *
142     * @param  Service  $service
143     * @param  Customer $customer
144     * @return JsonResponse
145     * @throws CrmException If removing the customer off the DB failed.
146     */
147    public function destroy(Service $service, Customer $customer): JsonResponse
148    {
149        // * Make sure we are accessing a customer that belongs to our service.
150        $this->authorize('customerBelongsToService', $customer);
151
152        // TODO: Gestion avec mesBR.
153        /** @var \App\Models\CustomerInfo */
154        $customerInfo = $customer->info($service->brand, Customer::FIRST_OR_FAIL);
155        /** @var \App\Models\CustomerLogin */
156        $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
157
158        DB::transaction(function () use ($service, $customer, $customerLogin, $customerInfo) {
159            if (
160                $customerInfo->delete() === false ||
161                $customerLogin->delete() === false
162            ) {
163                // @codeCoverageIgnoreStart
164                throw new CrmException('Api failed to delete customer.');
165                // @codeCoverageIgnoreEnd
166            };
167
168            $customer->optInHistory()->whereHas('brand', function ($q) use ($service) {
169                $q->where('id', $service->brand_id);
170            })->delete();
171
172            // * Force loading real relation counts.
173            $customer->unsetRelation('logins');
174            $customer->unsetRelation('infos');
175            $logins = $customer->logins()->count();
176            $infos  = $customer->infos()->count();
177
178            // If customer is not associated with any brand then delete.
179            if (!$logins and !$infos) {
180                if ($customer->delete() === false) {
181                    // @codeCoverageIgnoreStart
182                    throw new CrmException('Api failed to update customer.');
183                    // @codeCoverageIgnoreEnd
184                }
185            }
186        });
187
188        return response()->json();
189    }
190
191    /**
192     * Authenticate the user
193     * @param AuthenticateRequest $request
194     * @param Service             $service
195     * @return JsonResource
196     * @throws ApiErrorException With ApiError::authFailed() in case of wrong credentials.
197     * @throws ApiErrorException With ApiError::customerNotActivated() if customer account is not activated.
198     */
199    public function authenticate(AuthenticateRequest $request, Service $service): JsonResource
200    {
201        try {
202            /** @var \App\Models\Customer */
203            $customer = Customer::where('email', $request->email)->firstOrFail();
204
205            /** @var \App\Models\CustomerLogin $customerLogin */
206            $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
207            if (!$customerLogin->passwordMatch($request->password)) {
208                throw new ApiErrorException(ApiError::authFailed());
209            }
210
211            if (!$customerLogin->status) {
212                throw new ApiErrorException(ApiError::customerNotActivated());
213            }
214
215            UpdateCustomerLastConnection::dispatch($customer, $customerLogin, now())
216                ->onConnection('database')->afterResponse();
217
218            return new CustomerResource($service->brand, $customer);
219        } catch (ModelNotFoundException $e) {
220            throw new ApiErrorException(ApiError::authFailed());
221        }//end try
222    }
223
224    /**
225     * Activate the account of the user
226     * @param Customer $customer
227     * @param Service  $service
228     * @return \Illuminate\Http\JsonResponse
229     * @throws ApiErrorException With ApiError::customerAlreadyActive if customer account is already active.
230     * @throws CrmException If saving the customer to DB failed.
231     */
232    public function activateAccount(Customer $customer, Service $service)
233    {
234        // * Make sure we are accessing a customer that belongs to our service.
235        $this->authorize('customerBelongsToService', $customer);
236
237        /** @var \App\Models\CustomerLogin $login */
238        $login = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
239
240        if ($login->status) {
241            throw new ApiErrorException(ApiError::customerAlreadyActive());
242        }
243
244        $login->status = true;
245        if (!$login->save()) {
246            // @codeCoverageIgnoreStart
247            throw new CrmException('Failed to save login');
248            // @codeCoverageIgnoreEnd
249        }
250        return response()->json();
251    }
252}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V2/OptinController.php.html b/tests/coverage/Http/Controllers/Api/V2/OptinController.php.html new file mode 100644 index 0000000..c5a87bd --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V2/OptinController.php.html @@ -0,0 +1,208 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api/V2/OptinController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
OptinController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
 optin
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Enums\ApiError;
6use App\Exceptions\Api\V2\ApiErrorException;
7use App\Exceptions\Api\V2\CrmException;
8use App\Http\Controllers\Controller;
9use App\Http\Requests\Api\V2\SingleOptinRequest;
10use App\Models\CustomerInfo;
11use App\Models\Service;
12use App\Models\SingleOptin;
13
14class OptinController extends Controller
15{
16    /**
17     * Creates a new Single optin or destroys it if exists
18     *
19     * @param SingleOptinRequest $request
20     * @param Service            $service
21     * @return JsonResponse
22     * @throws ApiErrorException With ApiError::singleOptinCustomerExists if this email already has a full account.
23     * @throws CrmException If the single optin could not be added or removed.
24     */
25    public function optin(SingleOptinRequest $request, Service $service)
26    {
27        if (CustomerInfo::getFromEmail($service->brand, $request->email)->exists()) {
28            throw new ApiErrorException(ApiError::singleOptinCustomerExists());
29        }
30
31        /** @var \App\Models\SingleOptin */
32        $singleOptin = SingleOptin::where('email', $request->email)
33        ->whereHas('brand', function ($q) use ($service) {
34            $q->whereId($service->brand->id);
35        })->firstOrNew();
36
37        if ($request->opt_in) {
38            $singleOptin->fill($request->validated());
39            $singleOptin->brand()->associate($service->brand);
40            $success = $singleOptin->save();
41        } else {
42            $success = $singleOptin->delete() !== false;
43        }
44
45        if (!$success) {
46            // @codeCoverageIgnoreStart
47            throw new CrmException('Api failed to update single optin.');
48            // @codeCoverageIgnoreEnd
49        }
50
51        return response()->json();
52    }
53}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V2/QuestionController.php.html b/tests/coverage/Http/Controllers/Api/V2/QuestionController.php.html new file mode 100644 index 0000000..72be508 --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V2/QuestionController.php.html @@ -0,0 +1,261 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api/V2/QuestionController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
QuestionController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
 index
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 indexWithResponse
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 unAnsweredBy
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Http\Controllers\Controller;
6use App\Http\Resources\Api\V2\QuestionsResource;
7use App\Models\Answer;
8use App\Models\Customer;
9use App\Models\Question;
10use App\Models\Service;
11use Illuminate\Http\Resources\Json\JsonResource;
12use Illuminate\Http\Resources\Json\ResourceCollection;
13
14class QuestionController extends Controller
15{
16    /**
17     * Returns all service questions.
18     *
19     * @param  Service $service
20     * @return JsonResource
21     */
22    public function index(Service $service): ResourceCollection
23    {
24        return new QuestionsResource($service->brand->questions);
25    }
26
27    /**
28     * Returns all service questions with user answers for all of them.
29     *
30     * @param  Service  $service
31     * @param  Customer $customer
32     * @return JsonResource
33     */
34    public function indexWithResponse(Service $service, Customer $customer): ResourceCollection
35    {
36        // * Make sure we are accessing a customer that belongs to our service.
37        $this->authorize('customerBelongsToService', $customer);
38
39        $customerAnswers = $customer->getAnswers($service);
40        return new QuestionsResource($service->brand->questions, $customerAnswers);
41    }
42
43    /**
44     * Returns all service questions that was not answered by a user.
45     *
46     * @param  Service  $service
47     * @param  Customer $customer
48     * @return JsonResource
49     */
50    public function unAnsweredBy(Service $service, Customer $customer): JsonResource
51    {
52        // * Make sure we are accessing a customer that belongs to our service.
53        $this->authorize('customerBelongsToService', $customer);
54
55        $questions = Question::whereHas('brand', function ($q) use ($service) {
56            $q->where('id', $service->brand->id);
57        })->whereDoesntHave('answers', function ($q) use ($customer) {
58            $q->whereIn('id', Answer::whereHas('customers', function ($q) use ($customer) {
59                $q->where('id', $customer->id);
60            })->pluck('id'));
61        })->with('answers')->get();
62        return new QuestionsResource($questions);
63    }
64}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V2/StatsController.php.html b/tests/coverage/Http/Controllers/Api/V2/StatsController.php.html new file mode 100644 index 0000000..67f6475 --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V2/StatsController.php.html @@ -0,0 +1,306 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api/V2/StatsController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
37 / 37
StatsController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
5
+
+ 100.00% covered (success) +
+
+
100.00%
37 / 37
 brandStats
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 questionStats
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
 answersByDays
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
27 / 27
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Http\Controllers\Controller;
6use App\Http\Requests\Api\V2\GetBrandStatsRequest;
7use App\Http\Requests\Api\V2\GetQuestionStatsRequest;
8use App\Http\Resources\Api\V2\BrandStatsResource;
9use App\Http\Resources\Api\V2\QuestionStatsResource;
10use App\Models\BrandStat;
11use App\Models\CustomerAnswer;
12use App\Models\Question;
13use App\Models\Service;
14use Illuminate\Http\Resources\Json\ResourceCollection;
15use Illuminate\Support\Carbon;
16use Illuminate\Support\Collection;
17
18class StatsController extends Controller
19{
20    /**
21     * Get specific brand statistics
22     *
23     * @param GetBrandStatsRequest $request
24     * @param Service              $service
25     * @return ResourceCollection
26     */
27    public function brandStats(GetBrandStatsRequest $request, Service $service): ResourceCollection
28    {
29        $stats = BrandStat::whereDate('created_at', '>=', $request->start)
30            ->whereDate('created_at', '<=', $request->end)
31            ->whereBrandId($service->brand->id)->get();
32        return new BrandStatsResource($stats);
33    }
34
35    /**
36     * Get statistics for a specific question
37     *
38     * @param GetQuestionStatsRequest $request
39     * @param Question                $question
40     * @return QuestionStatsResource
41     */
42    public function questionStats(GetQuestionStatsRequest $request, Question $question): QuestionStatsResource
43    {
44        // * Make sure we are accessing a customer that belongs to our service.
45        $this->authorize('questionBelongsToService', $question);
46
47        $answers = $question->answers;
48        $start   = Carbon::parse($request->start);
49        $end     = Carbon::parse($request->end);
50
51        return new QuestionStatsResource(
52            $this->answersByDays($answers, $start, $end),
53            $question
54        );
55    }
56
57    /**
58     * Total réponses par dates
59     *
60     * @param Collection $answers
61     * @param Carbon     $start
62     * @param Carbon     $end
63     * @return Collection
64     */
65    private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
66    {
67        // * Totalize by date all answers.
68        $answersByDays = CustomerAnswer::whereDate('created_at', '>=', $start)
69            ->whereDate('created_at', '<=', $end)
70            ->whereIn('answer_id', $answers->pluck('id'))
71            ->get()->map(function ($custAnswer) {
72                return [
73                    'answer_id' => $custAnswer->answer_id,
74                    'created_at' => $custAnswer->created_at->format('Y-m-d'),
75                ];
76            })->groupBy(['created_at', 'answer_id'])->map(function ($day) {
77                return $day->map(function ($custAnswers) {
78                    return $custAnswers->count();
79                });
80            });
81
82        do {
83            $currentDate = $start->format('Y-m-d');
84            /** @var \Illuminate\Support\Collection $answersByDays */
85            if (!$answersByDays->has($currentDate)) {
86                $answersByDays->put(
87                    $currentDate,
88                    $answers->mapWithKeys(function ($answer) {
89                        return [$answer->id => 0];
90                    })
91                );
92            } else {
93                $curentAnswers = $answersByDays->pull($currentDate);
94                $answersByDays->put(
95                    $currentDate,
96                    $answers->mapWithKeys(function ($answer) use ($curentAnswers) {
97                        return [$answer->id =>
98                        $curentAnswers->has($answer->id) ?
99                            $curentAnswers->get($answer->id) : 0];
100                    })
101                );
102            }
103
104            $start = $start->addDay();
105        } while ($start->lessThanOrEqualTo($end));
106
107        return $answersByDays;
108    }
109}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V2/dashboard.html b/tests/coverage/Http/Controllers/Api/V2/dashboard.html new file mode 100644 index 0000000..bd9bea5 --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V2/dashboard.html @@ -0,0 +1,287 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Controllers/Api/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/V2/index.html b/tests/coverage/Http/Controllers/Api/V2/index.html new file mode 100644 index 0000000..3fd91fe --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/V2/index.html @@ -0,0 +1,290 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api/V2 + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
AnswerController.php
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiController.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ArcadiaController.php
+
+ 100.00% covered (success) +
+
+
100.00%
31 / 31
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CustomerController.php
+
+ 100.00% covered (success) +
+
+
100.00%
77 / 77
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
OptinController.php
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
QuestionController.php
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
StatsController.php
+
+ 100.00% covered (success) +
+
+
100.00%
37 / 37
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/Http/Controllers/Api/dashboard.html b/tests/coverage/Http/Controllers/Api/dashboard.html new file mode 100644 index 0000000..82e3be8 --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/dashboard.html @@ -0,0 +1,286 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Controllers/Api + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Api/index.html b/tests/coverage/Http/Controllers/Api/index.html new file mode 100644 index 0000000..55d26b9 --- /dev/null +++ b/tests/coverage/Http/Controllers/Api/index.html @@ -0,0 +1,134 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Api + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
V1
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
V2
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ +
+ + diff --git a/tests/coverage/Http/Controllers/BrandController.php.html b/tests/coverage/Http/Controllers/BrandController.php.html new file mode 100644 index 0000000..726d758 --- /dev/null +++ b/tests/coverage/Http/Controllers/BrandController.php.html @@ -0,0 +1,157 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/BrandController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
BrandController
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 index
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Http\Controllers\Controller;
6use App\Models\Brand;
7use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
8use Kwaadpepper\CrudPolicies\Traits\CrudController;
9
10/**
11 * @codeCoverageIgnore
12 */
13class BrandController extends Controller
14{
15    use CrudController {
16        index as crudIndex;
17    }
18
19    /** @var Brand */
20    public static $modelClass = Brand::class;
21
22    /**
23     * The Bo app layout
24     *
25     * @var string
26     */
27    public static $layout = 'layouts.app';
28
29    /**
30     * Overload crud-policies index
31     *
32     * @param CrudIndexRequest $request
33     * @return \Illuminate\Http\Response
34     */
35    public function index(CrudIndexRequest $request)
36    {
37        /** @var \Illuminate\View\View */
38        $view = $this->crudIndex($request);
39        if ($request->ajax()) {
40            return $view;
41        }
42        return view('brand.index', $view->getData());
43    }
44}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/Controller.php.html b/tests/coverage/Http/Controllers/Controller.php.html new file mode 100644 index 0000000..9fdc298 --- /dev/null +++ b/tests/coverage/Http/Controllers/Controller.php.html @@ -0,0 +1,120 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/Controller.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
Controller
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
6use Illuminate\Foundation\Bus\DispatchesJobs;
7use Illuminate\Foundation\Validation\ValidatesRequests;
8use Illuminate\Routing\Controller as BaseController;
9
10/**
11 * @codeCoverageIgnore
12 */
13class Controller extends BaseController
14{
15    use AuthorizesRequests;
16    use DispatchesJobs;
17    use ValidatesRequests;
18}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/CustomerController.php.html b/tests/coverage/Http/Controllers/CustomerController.php.html new file mode 100644 index 0000000..71c3bc3 --- /dev/null +++ b/tests/coverage/Http/Controllers/CustomerController.php.html @@ -0,0 +1,482 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/CustomerController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerController
n/a
0 / 0
n/a
0 / 0
32
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 index
n/a
0 / 0
13
n/a
0 / 0
 show
n/a
0 / 0
1
n/a
0 / 0
 delete
n/a
0 / 0
8
n/a
0 / 0
 export
n/a
0 / 0
1
n/a
0 / 0
 exportSubmit
n/a
0 / 0
8
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Enums\CustomerExportType;
6use App\Http\Requests\CustomerExport;
7use App\Models\Brand;
8use App\Models\Customer;
9use App\Models\CustomerInfo;
10use App\Models\CustomerLogin;
11use App\Models\SingleOptin;
12use Illuminate\Database\Query\JoinClause;
13use Illuminate\Http\Request;
14use Illuminate\Support\Carbon;
15use Illuminate\Support\Facades\DB;
16use Illuminate\Support\Facades\Schema;
17use Illuminate\Support\Facades\Session;
18use Illuminate\Support\Facades\View;
19use Illuminate\Support\Str;
20use League\Csv\Writer;
21use Symfony\Component\HttpFoundation\StreamedResponse;
22
23/**
24 * @codeCoverageIgnore
25 */
26class CustomerController extends Controller
27{
28    /**
29     * View share crud policies modelClass
30     */
31    public function __construct()
32    {
33        $this->middleware(function ($request, $next) {
34            View::share('modelClass', Customer::class);
35            return $next($request);
36        });
37    }
38
39    /**
40     * View all customers
41     *
42     * @param Request $request
43     * @return \Illuminate\Http\Response
44     */
45    public function index(Request $request)
46    {
47        $modelTable = (new Customer())->getTable();
48        $modelClass = Customer::class;
49        $brands     = Brand::select('id', 'name')->get();
50        $columns    = ['customer_infos.lastname', 'customer_infos.firstname'];
51
52        // * Jointure pour pouvoir trier les colonnes.
53        /** @var \Illuminate\Database\Eloquent\Builder */
54        $query = CustomerInfo::join('customer_logins', function (JoinClause $join) {
55            $join->on('customer_logins.brand_id', '=', 'customer_infos.brand_id')
56            ->on('customer_logins.customer_id', '=', 'customer_infos.customer_id');
57        })
58        ->join('customers', function (JoinClause $join) {
59            $join->on('customers.id', '=', 'customer_infos.customer_id');
60        })
61        ->join('brands', function (JoinClause $join) {
62            $join->on('brands.id', '=', 'customer_infos.brand_id');
63        });
64        if (request()->brand or Session::has("crud.$modelTable.brand")) {
65            $brand = $request->brand ?? Session::get("crud.$modelTable.brand");
66            Session::put("crud.$modelTable.brand", $brand);
67            $query = $query->where('customer_infos.brand_id', $brand);
68        }
69
70        // * Search on crud Label column.
71        if ($request->search or Session::has("crud.$modelTable.search")) {
72            $search = $request->search ?? Session::get("crud.$modelTable.search");
73            Session::put("crud.$modelTable.search", $search);
74            $query->where(function ($q) use ($columns, $search) {
75                foreach ($columns as $column) {
76                    $q = $q->orWhere($column, 'LIKE', "%$search%");
77                }
78                $q = $q->orWhere('customers.email', 'LIKE', "%$search%");
79                $q = $q->orWhere('customer_logins.last_connection', 'LIKE', "%$search%");
80            });
81        }
82
83        // * Sort columns.
84        if ($request->sort_col) {
85            Session::put("crud.$modelTable.sort_col", $request->sort_col);
86        }
87        if ($request->sort_way) {
88            Session::put("crud.$modelTable.sort_way", $request->sort_way);
89        }
90
91        if ($request->rst) {
92            Session::remove("crud.$modelTable.brand");
93            Session::remove("crud.$modelTable.search");
94            Session::remove("crud.$modelTable.sort_way");
95            Session::remove("crud.$modelTable.sort_col");
96        }
97        if (
98            Session::has("crud.$modelTable.sort_col") and
99            Session::has("crud.$modelTable.sort_way") and
100            (Schema::hasColumn($modelTable, Session::get("crud.$modelTable.sort_col")) or
101            in_array(Session::get("crud.$modelTable.sort_col"), [
102                'brands.name',
103                'customers.email',
104                'customer_infos.firstname',
105                'customer_infos.lastname',
106                'customer_infos.opt_in',
107                'customer_logins.last_connection',
108                'customer_logins.status'
109            ]))
110        ) {
111            $query = $query->orderBy(
112                Session::get("crud.$modelTable.sort_col"),
113                Session::get("crud.$modelTable.sort_way")
114            );
115        }
116
117        /** @var \Illuminate\Pagination\AbstractPaginator */
118        $customers = $query->paginate(config('crud.paginate', 50));
119
120        return view('customer.index', compact(
121            'modelClass',
122            'modelTable',
123            'customers',
124            'brands'
125        ));
126    }
127
128    /**
129     * Show a specific custmer
130     *
131     * @param Customer $customer
132     * @param Brand    $brand
133     * @return \Illuminate\Http\Response
134     */
135    public function show(Customer $customer, Brand $brand)
136    {
137        $customerInfos = CustomerInfo::where('customer_id', $customer->id)
138            ->where('brand_id', $brand->id)->firstOrFail();
139        $customerLogin = CustomerLogin::where('customer_id', $customer->id)
140            ->where('brand_id', $brand->id)->first();
141
142        return view('customer.show', compact(
143            'customerInfos',
144            'customerLogin'
145        ));
146    }
147
148    /**
149     * Delete a specific customer info
150     *
151     * @param Customer $customer
152     * @param Brand    $brand
153     * @return \Illuminate\Http\RedirectResponse
154     */
155    public function delete(Customer $customer, Brand $brand)
156    {
157        try {
158            // TODO: Gestion avec mesBR.
159            /** @var \App\Models\CustomerInfo */
160            $customerInfo = $customer->info($brand, Customer::FIRST_OR_FAIL);
161            /** @var \App\Models\CustomerLogin */
162            $customerLogin = $customer->login($brand, Customer::FIRST_OR_FAIL);
163
164            DB::transaction(function () use (
165                $brand,
166                $customer,
167                $customerLogin,
168                $customerInfo
169            ) {
170                if (
171                    $customerInfo->delete() === false ||
172                    $customerLogin->delete() === false
173                ) {
174                    throw new \ErrorException('Api failed to delete customer.');
175                };
176
177                $customer->optInHistory()->whereHas('brand', function ($q) use ($brand) {
178                    $q->where('id', $brand->id);
179                })->delete();
180
181                $logins  = $customer->logins()->count();
182                $infos   = $customer->infos()->count();
183                $history = $customer->optInHistory()->count();
184
185                // If customer is not associated with any brand then delete.
186                if (!$logins and !$infos and !$history) {
187                    if ($customer->delete() === false) {
188                        throw new \ErrorException('Api failed to update customer.');
189                    }
190                }
191            });
192        } catch (\ErrorException $e) {
193            return redirect()->route('customers.index')
194            ->with('error', trans('Le compte n\'as pas pu être supprimé.'));
195        } //end try
196
197        return redirect()->route('customers.index')->with('warning', trans('Compte supprimé.'));
198    }
199
200    /**
201     * Show export the customers form
202     *
203     * @return \Illuminate\Http\Response
204     */
205    public function export()
206    {
207        $brands = Brand::all();
208        return view('customer.export', compact(
209            'brands'
210        ));
211    }
212
213    /**
214     * Exports customers following the requested export type
215     *
216     * @param CustomerExport $request
217     * @return \Symfony\Component\HttpFoundation\StreamedResponse
218     * @throws \Exception If the request export type in not handled.
219     */
220    public function exportSubmit(CustomerExport $request)
221    {
222        $pagination = 2000;
223        $header     = '"lastname";"firstname";"email";"hashcode"';
224
225        $w = Writer::createFromString($header . PHP_EOL);
226        $w->setDelimiter(';');
227        $w->setEnclosure('"');
228        $w->setOutputBOM(Writer::BOM_UTF8);
229
230        $exportType = CustomerExportType::make(\intval($request->export_type));
231
232        // * We send the CSV using Symfony StreamedResponse.
233        $response = new StreamedResponse();
234        $response->headers->set('Content-Encoding', 'none');
235        $response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
236        $response->headers->set('Description', 'File Transfer');
237        $response->headers->set(
238            'Content-Disposition',
239            sprintf(
240                'attachment; filename="Export pour newsletter %s %s.csv"',
241                Carbon::now()->format('d-m-Y_H-i'),
242                Str::slug($exportType)
243            )
244        );
245
246        switch ($exportType) {
247            case CustomerExportType::both():
248                $continue = true;
249                // ! Continue
250            case CustomerExportType::customer():
251                $page = 1;
252                /** @var \Illuminate\Database\Eloquent\Builder $req */
253                $req   = CustomerInfo::where('brand_id', $request->brand)
254                ->where('opt_in', true);
255                $total = $req->count();
256                $pages = \ceil($total / $pagination);
257                do {
258                    /** @var \Illuminate\Pagination\LengthAwarePaginator $items */
259                    $items = $req->with('customer', function ($q) {
260                        $q->select(['id', 'email']);
261                    })->paginate(
262                        $pagination,
263                        ['lastname', 'firstname', 'hashcode', 'customer_id'],
264                        'page',
265                        $page
266                    );
267                    $items->map(function (CustomerInfo $customerInfo) use ($w) {
268                        $w->insertOne([
269                            'lastname' => $customerInfo->lastname,
270                            'firstname' => $customerInfo->firstname,
271                            'email' => $customerInfo->customer->email,
272                            'hashcode' => $customerInfo->hashcode
273                        ]);
274                    });
275                } while ($page++ <= $pages);
276                if (!isset($continue)) {
277                    break;
278                }
279                // ! Continue
280            case CustomerExportType::singleOptin():
281                SingleOptin::where('brand_id', $request->brand)->get()
282                    ->map(function (SingleOptin $singleOptin) use ($w) {
283                        $w->insertOne([
284                            'lastname' => '',
285                            'firstname' => '',
286                            'email' => $singleOptin->email,
287                            'hashcode' => ''
288                        ]);
289                    });
290                break;
291            default:
292                throw new \Exception(sprintf('Unhandled CustomerExportType %s', $exportType->label));
293        } //end switch
294
295        /*
296            We create a callable to output the CSV in chunk
297            with Symfony StreamResponse you can flush the body content if necessary
298            see Symfony documentation for more information
299        */
300
301        // * The flush value should depend on your CSV size.
302        $flush_threshold = 1000;
303
304        $response->setCallback(function () use ($w, $flush_threshold) {
305            foreach ($w->chunk(1024) as $offset => $chunk) {
306                echo $chunk;
307                if ($offset % $flush_threshold === 0) {
308                    flush();
309                }
310            }
311        });
312        return $response;
313    }
314}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/LoginController.php.html b/tests/coverage/Http/Controllers/LoginController.php.html new file mode 100644 index 0000000..2089265 --- /dev/null +++ b/tests/coverage/Http/Controllers/LoginController.php.html @@ -0,0 +1,189 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/LoginController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
LoginController
n/a
0 / 0
n/a
0 / 0
5
n/a
0 / 0
 index
n/a
0 / 0
1
n/a
0 / 0
 authenticate
n/a
0 / 0
3
n/a
0 / 0
 logout
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use Illuminate\Http\Request;
6use Illuminate\Support\Facades\Auth;
7
8/**
9 * @codeCoverageIgnore
10 */
11class LoginController extends Controller
12{
13    /**
14     * Shows the login view
15     *
16     * @return \Illuminate\Http\Response
17     */
18    public function index()
19    {
20        return view('login');
21    }
22
23    /**
24     * Undocumented function
25     *
26     * @param Request $request
27     * @return \Illuminate\Http\Response
28     */
29    public function authenticate(Request $request)
30    {
31        if (
32            Auth::attempt(
33                ["email" => $request->email, "password" => $request->password],
34                $request->remember !== null ? true : false
35            )
36        ) {
37            return redirect()->route('customers.index');
38        } else {
39            return back()->withInput($request->except("password"))
40                ->withErrors("Les identifiants entrés sont incorrects, veuillez réessayer.");
41        }
42    }
43
44    /**
45     * Undocumented function
46     *
47     * @return \Illuminate\Http\Response
48     */
49    public function logout()
50    {
51        Auth::logout();
52        return redirect()->route('login');
53    }
54}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/QuestionController.php.html b/tests/coverage/Http/Controllers/QuestionController.php.html new file mode 100644 index 0000000..30976d7 --- /dev/null +++ b/tests/coverage/Http/Controllers/QuestionController.php.html @@ -0,0 +1,305 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/QuestionController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
QuestionController
n/a
0 / 0
n/a
0 / 0
12
n/a
0 / 0
 indexQuery
n/a
0 / 0
2
n/a
0 / 0
 index
n/a
0 / 0
2
n/a
0 / 0
 export
n/a
0 / 0
1
n/a
0 / 0
 exportSubmit
n/a
0 / 0
7
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Http\Controllers\Controller;
6use App\Http\Requests\QuestionExport;
7use App\Models\Brand;
8use App\Models\Question;
9use Carbon\Carbon;
10use Illuminate\Database\Eloquent\Builder;
11use Illuminate\Support\Facades\DB;
12use Illuminate\Support\Str;
13use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
14use Kwaadpepper\CrudPolicies\Traits\CrudController;
15use League\Csv\Writer;
16use Symfony\Component\HttpFoundation\StreamedResponse;
17
18/**
19 * @codeCoverageIgnore
20 */
21class QuestionController extends Controller
22{
23    use CrudController {
24        index as crudIndex;
25    }
26
27    /** @var Question */
28    public static $modelClass = Question::class;
29
30    /**
31     * The Bo app layout
32     *
33     * @var string
34     */
35    public static $layout = 'layouts.app';
36
37    /**
38     * Event index crud-policies
39     *
40     * @param Builder $query
41     * @return Builder
42     */
43    public function indexQuery(Builder $query): Builder
44    {
45        if (request()->brand) {
46            $query = $query->where('brand_id', '=', request()->brand);
47        }
48        return $query;
49    }
50
51    /**
52     * Overload crud-policies index
53     *
54     * @param CrudIndexRequest $request
55     * @return \Illuminate\Http\Response
56     */
57    public function index(CrudIndexRequest $request)
58    {
59        /** @var \Illuminate\View\View */
60        $view = $this->crudIndex($request);
61        if ($request->ajax()) {
62            return $view;
63        }
64        return view('question.index', $view->getData());
65    }
66
67    /**
68     * Shows the question export form
69     *
70     * @return \Illuminate\Http\Response
71     */
72    public function export()
73    {
74        $brands = Brand::all();
75        return view('question.export', compact('brands'));
76    }
77
78    /**
79     * Exports questions as csv the requested export type
80     *
81     * @param QuestionExport $request
82     * @return \Symfony\Component\HttpFoundation\StreamedResponse
83     */
84    public function exportSubmit(QuestionExport $request)
85    {
86        $results   = [];
87        $brand     = Brand::findOrFail($request->brand);
88        $questions = $brand->questions->load('answers');
89
90        foreach ($questions as $question) {
91            $answersCounts = DB::query()
92                ->selectRaw('count(*) as total, answer_id')
93                ->from('customer_answer')
94                ->whereIn('answer_id', $question->answers->pluck('id'))
95                ->groupBy('answer_id')
96                ->get()->keyBy('answer_id');
97
98            $results[$question->id] = [
99                'question' => $question->question,
100                'answers' => []
101            ];
102            foreach ($question->answers as $answer) {
103                $results[$question->id]['answers'][] =
104                    [
105                        "$answer->answer ",
106                        $answersCounts[$answer->id]->total ?? 0
107                    ];
108            }
109        }
110
111        $w = Writer::createFromString('');
112        $w->setDelimiter(';');
113        $w->setEnclosure('"');
114        $w->setOutputBOM(Writer::BOM_UTF8);
115
116        // * We send the CSV using Symfony StreamedResponse.
117        $response = new StreamedResponse();
118        $response->headers->set('Content-Encoding', 'none');
119        $response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
120        $response->headers->set('Description', 'File Transfer');
121        $response->headers->set(
122            'Content-Disposition',
123            sprintf(
124                'attachment; filename="Export pour newsletter %s %s.csv"',
125                Carbon::now()->format('d-m-Y_H-i'),
126                Str::slug($brand->name)
127            )
128        );
129
130        foreach ($results as $result) {
131            $w->insertOne([
132                'question' => $result['question'],
133            ]);
134            foreach ($result['answers'] as $answer) {
135                $w->insertOne($answer);
136            }
137            $w->insertOne([]);
138        }
139
140        /*
141            We create a callable to output the CSV in chunk
142            with Symfony StreamResponse you can flush the body content if necessary
143            see Symfony documentation for more information
144        */
145
146        // * The flush value should depend on your CSV size.
147        $flush_threshold = 1000;
148
149        $response->setCallback(function () use ($w, $flush_threshold) {
150            foreach ($w->chunk(1024) as $offset => $chunk) {
151                echo $chunk;
152                if ($offset % $flush_threshold === 0) {
153                    flush();
154                }
155            }
156        });
157        return $response;
158    }
159}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/ServiceController.php.html b/tests/coverage/Http/Controllers/ServiceController.php.html new file mode 100644 index 0000000..87f856f --- /dev/null +++ b/tests/coverage/Http/Controllers/ServiceController.php.html @@ -0,0 +1,196 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/ServiceController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ServiceController
n/a
0 / 0
n/a
0 / 0
4
n/a
0 / 0
 index
n/a
0 / 0
2
n/a
0 / 0
 generateServicePassword
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Http\Requests\GenerateServiceToken;
6use App\Mail\ServiceCredentialRenewed;
7use App\Models\Service;
8use Illuminate\Support\Facades\Hash;
9use Illuminate\Support\Facades\Mail;
10use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
11use Kwaadpepper\CrudPolicies\Traits\CrudController;
12
13/**
14 * @codeCoverageIgnore
15 */
16class ServiceController extends Controller
17{
18    use CrudController {
19        index as crudIndex;
20    }
21
22    /** @var Service */
23    public static $modelClass = Service::class;
24
25    /**
26     * The Bo app layout
27     *
28     * @var string
29     */
30    public static $layout = 'layouts.app';
31
32    /**
33     * Overload crud-policies index
34     *
35     * @param CrudIndexRequest $request
36     * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
37     */
38    public function index(CrudIndexRequest $request)
39    {
40        /** @var \Illuminate\View\View */
41        $view = $this->crudIndex($request);
42        if ($request->ajax()) {
43            return $view;
44        }
45        return view('service.index', $view->getData());
46    }
47
48    /**
49     * Regen service credentials
50     *
51     * @param GenerateServiceToken $request
52     * @param Service              $service
53     * @return \Illuminate\Http\RedirectResponse
54     */
55    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed, Squiz.Commenting.FunctionComment.Missing
56    public function generateServicePassword(GenerateServiceToken $request, Service $service)
57    {
58        /** @var \App\Models\User $user */
59        $user = auth()->user();
60
61        // * Special Condition for migration from v2 to v3.
62        $service->bearer_token = !Hash::needsRehash($service->bearer_token) ?
63            $service->generateBearerToken() : $service->bearer_token;
64
65        $bearerPwd = $service->generateBearerPassword();
66        $service->saveOrFail();
67        Mail::send(new ServiceCredentialRenewed($service, $user));
68        return back()
69            // phpcs:ignore Generic.Files.LineLength.TooLong
70            ->with('error', "ATTENTION : METTEZ À JOUR LE SITE ! \nNouveau token d'API de $service->name : '$service->bearer_token' et mot de passe '$bearerPwd'");
71    }
72}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/SingleOptinController.php.html b/tests/coverage/Http/Controllers/SingleOptinController.php.html new file mode 100644 index 0000000..d3f90e0 --- /dev/null +++ b/tests/coverage/Http/Controllers/SingleOptinController.php.html @@ -0,0 +1,182 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/SingleOptinController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
SingleOptinController
n/a
0 / 0
n/a
0 / 0
4
n/a
0 / 0
 indexQuery
n/a
0 / 0
2
n/a
0 / 0
 index
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Models\SingleOptin;
6use Illuminate\Database\Eloquent\Builder;
7use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
8use Kwaadpepper\CrudPolicies\Traits\CrudController;
9
10/**
11 * @codeCoverageIgnore
12 */
13class SingleOptinController extends Controller
14{
15    use CrudController {
16        index as crudIndex;
17    }
18
19    /** @var SingleOptin */
20    public static $modelClass = SingleOptin::class;
21
22    /**
23     * The Bo app layout
24     *
25     * @var string
26     */
27    public static $layout = 'layouts.app';
28
29    /**
30     * Event index crud-policies
31     *
32     * @param Builder $query
33     * @return Builder
34     */
35    public function indexQuery(Builder $query): Builder
36    {
37        if (request()->brand) {
38            $query = $query->where('brand_id', request()->brand);
39        }
40        return $query;
41    }
42
43    /**
44     * Overload crud-policies index
45     *
46     * @param CrudIndexRequest $request
47     * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
48     */
49    public function index(CrudIndexRequest $request)
50    {
51        /** @var \Illuminate\View\View */
52        $view = $this->crudIndex($request);
53        if ($request->ajax()) {
54            return $view;
55        }
56        return view('single_optin.index', $view->getData());
57    }
58}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/StatsController.php.html b/tests/coverage/Http/Controllers/StatsController.php.html new file mode 100644 index 0000000..fc46645 --- /dev/null +++ b/tests/coverage/Http/Controllers/StatsController.php.html @@ -0,0 +1,355 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers/StatsController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StatsController
n/a
0 / 0
n/a
0 / 0
12
n/a
0 / 0
 viewStatsByBrand
n/a
0 / 0
2
n/a
0 / 0
 exportByBrandCsv
n/a
0 / 0
3
n/a
0 / 0
 viewByQuestion
n/a
0 / 0
2
n/a
0 / 0
 exportByQuestionCsv
n/a
0 / 0
2
n/a
0 / 0
 answersByDays
n/a
0 / 0
3
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Http\Requests\StatsByBrandRequest;
6use App\Http\Requests\StatsByQuestionRequest;
7use App\Models\Brand;
8use App\Models\BrandStat;
9use App\Models\CustomerAnswer;
10use App\Models\Question;
11use Illuminate\Support\Carbon;
12use Illuminate\Support\Collection;
13use Illuminate\Support\Facades\Schema;
14use League\Csv\Writer;
15
16/**
17 * @codeCoverageIgnore
18 */
19class StatsController extends Controller
20{
21    /**
22     * Shows stats by brand
23     *
24     * @param StatsByBrandRequest $request
25     * @return \Illuminate\Http\Response
26     */
27    public function viewStatsByBrand(StatsByBrandRequest $request)
28    {
29        $stats = null;
30        if ($request->post()) {
31            $stats = BrandStat::whereDate('created_at', '>=', $request->start)
32                ->whereDate('created_at', '<=', $request->end)
33                ->whereBrandId($request->brand)->get();
34        }
35        return view('stats.view-stats-by-brands', \compact('stats'));
36    }
37
38    /**
39     * Exports stats by brand as a csv file
40     *
41     * @param StatsByBrandRequest $request
42     * @return \Illuminate\Http\Response
43     */
44    public function exportByBrandCsv(StatsByBrandRequest $request)
45    {
46        $brand = Brand::findOrFail($request->brand);
47        $stats = BrandStat::with('brand')->whereDate('created_at', '>=', $request->start)
48            ->whereDate('created_at', '<=', $request->end)
49            ->whereBrandId($request->brand)->get();
50
51        $columns = collect(Schema::getColumnListing((new BrandStat())->getTable()))
52            ->filter(function ($value) {
53                return !in_array($value, ['created_at', 'brand_id']);
54            })->all();
55
56        $header = '"date";"marque";' . implode(';', $columns);
57
58        $w = Writer::createFromString($header . PHP_EOL);
59        $w->setDelimiter(';');
60        $w->setEnclosure('"');
61        $w->setOutputBOM(Writer::BOM_UTF8);
62
63        foreach ($stats as $stat) {
64            $cols = [
65                $stat->created_at->format('Y-m-d'),
66                $stat->brand->name
67            ];
68            foreach ($columns as $colname) {
69                $cols[] = $stat->{$colname};
70            }
71            $w->insertOne($cols);
72        }
73        return response((string)$w, 200, [
74            'Content-Encoding' => 'none',
75            'Content-Type' => 'text/csv; charset=UTF-8',
76            // phpcs:ignore Generic.Files.LineLength.TooLong
77            'Content-Disposition' => "attachment; filename=\"Statistiques du $request->start au $request->end pour $brand->name.csv\"",
78            'Content-Description' => 'File Transfer',
79        ]);
80    }
81
82    /**
83     * Views stats for a specific question
84     *
85     * @param StatsByQuestionRequest $request
86     * @return \Illuminate\Http\Response
87     */
88    public function viewByQuestion(StatsByQuestionRequest $request)
89    {
90        $question      = null;
91        $answers       = null;
92        $answersByDays = null;
93        if ($request->post()) {
94            $question = Question::findOrFail($request->question);
95            $answers  = $question->answers;
96            $start    = Carbon::parse($request->start);
97            $end      = Carbon::parse($request->end);
98
99            $answersByDays = $this->answersByDays($answers, $start, $end);
100        }
101
102        return view('stats.view-stats-by-questions', \compact('question', 'answers', 'answersByDays'));
103    }
104
105    /**
106     * Exports stats for a specific question as csv
107     *
108     * @param StatsByQuestionRequest $request
109     * @return \Illuminate\Http\Response
110     */
111    public function exportByQuestionCsv(StatsByQuestionRequest $request)
112    {
113        $question = Question::findOrFail($request->question);
114        $answers  = $question->answers;
115        $start    = Carbon::parse($request->start);
116        $end      = Carbon::parse($request->end);
117
118        $answersByDays = $this->answersByDays($answers, $start, $end);
119
120        $header = '"date";' . implode(';', $answers->map(function ($answer) {
121            return "\"$answer->answer\"";
122        })->all());
123
124        $w = Writer::createFromString($header . PHP_EOL);
125        $w->setDelimiter(';');
126        $w->setEnclosure('"');
127        $w->setOutputBOM(Writer::BOM_UTF8);
128
129        foreach ($answersByDays as $day => $dayAnswers) {
130            $w->insertOne(array_merge(
131                [
132                    $day
133                ],
134                $dayAnswers->values()->all()
135            ));
136        }
137        return response((string)$w, 200, [
138            'Content-Encoding' => 'none',
139            'Content-Type' => 'text/csv; charset=UTF-8',
140            // phpcs:ignore Generic.Files.LineLength.TooLong
141            'Content-Disposition' => "attachment; filename=\"Statistiques du $request->start au $request->end pour $question->question.csv\"",
142            'Content-Description' => 'File Transfer',
143        ]);
144    }
145
146    /**
147     * Makes a collection of total users answers by day
148     *
149     * @param Collection $answers A collection of Answer.
150     * @param Carbon     $start   The wanted start period.
151     * @param Carbon     $end     The wanted end period.
152     * @return Collection A collection with date as Y_m_d index with array of answers and their total.
153     */
154    private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
155    {
156        // * Totalize by date all answers.
157        $answersByDays = CustomerAnswer::whereDate('created_at', '>=', $start)
158            ->whereDate('created_at', '<=', $end)
159            ->whereIn('answer_id', $answers->pluck('id'))
160            ->get()->map(function ($custAnswer) {
161                return [
162                    'answer_id' => $custAnswer->answer_id,
163                    'created_at' => $custAnswer->created_at->format('Y-m-d'),
164                ];
165            })->groupBy(['created_at', 'answer_id'])->map(function ($day) {
166                return $day->map(function ($custAnswers) {
167                    return $custAnswers->count();
168                });
169            });
170
171        do {
172            $currentDate = $start->format('Y-m-d');
173            /** @var \Illuminate\Support\Collection $answersByDays */
174            if (!$answersByDays->has($currentDate)) {
175                $answersByDays->put(
176                    $currentDate,
177                    $answers->mapWithKeys(function ($answer) {
178                        return [$answer->answer => 0];
179                    })
180                );
181            } else {
182                $curentAnswers = $answersByDays->pull($currentDate);
183                $answersByDays->put(
184                    $currentDate,
185                    $answers->mapWithKeys(function ($answer) use ($curentAnswers) {
186                        return [$answer->answer =>
187                        $curentAnswers->has($answer->id) ?
188                        $curentAnswers->get($answer->id) : 0];
189                    })
190                );
191            }
192
193            $start = $start->addDay();
194        } while ($start->lessThanOrEqualTo($end));
195
196        return $answersByDays;
197    }
198}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/dashboard.html b/tests/coverage/Http/Controllers/dashboard.html new file mode 100644 index 0000000..a2145f5 --- /dev/null +++ b/tests/coverage/Http/Controllers/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Controllers + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Controllers/index.html b/tests/coverage/Http/Controllers/index.html new file mode 100644 index 0000000..5a01e68 --- /dev/null +++ b/tests/coverage/Http/Controllers/index.html @@ -0,0 +1,237 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Controllers + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
Api
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
AnswerController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
BrandController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Controller.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
CustomerController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
LoginController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
QuestionController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ServiceController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
SingleOptinController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StatsController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Http/Kernel.php.html b/tests/coverage/Http/Kernel.php.html new file mode 100644 index 0000000..360e4b6 --- /dev/null +++ b/tests/coverage/Http/Kernel.php.html @@ -0,0 +1,170 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Kernel.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
Kernel
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http;
4
5use Illuminate\Foundation\Http\Kernel as HttpKernel;
6
7class Kernel extends HttpKernel
8{
9    /**
10     * The application's global HTTP middleware stack.
11     *
12     * These middleware are run during every request to your application.
13     *
14     * @var array
15     */
16    protected $middleware = [
17        \App\Http\Middleware\TrustProxies::class,
18        \Fruitcake\Cors\HandleCors::class,
19        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
20        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
21        \App\Http\Middleware\TrimStrings::class,
22        \App\Http\Middleware\XssSanitizer::class,
23        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class
24    ];
25
26    /**
27     * The application's route middleware groups.
28     *
29     * @var array
30     */
31    protected $middlewareGroups = [
32        'web' => [
33            \App\Http\Middleware\EncryptCookies::class,
34            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
35            \Illuminate\Session\Middleware\StartSession::class,
36            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
37            \App\Http\Middleware\VerifyCsrfToken::class,
38            \Illuminate\Routing\Middleware\SubstituteBindings::class,
39        ],
40
41        'api' => [
42            'throttle:api',
43            \App\Http\Middleware\JsonOnly::class,
44            \App\Http\Middleware\ApiResponse::class,
45            \App\Http\Middleware\ApiCheckTimeStamps::class,
46            \Illuminate\Routing\Middleware\SubstituteBindings::class
47        ],
48    ];
49
50    /**
51     * The application's route middleware.
52     *
53     * These middleware may be assigned to groups or used individually.
54     *
55     * @var array
56     */
57    protected $routeMiddleware = [
58        'auth' => \App\Http\Middleware\Authenticate::class,
59        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
60        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
61        'can' => \Illuminate\Auth\Middleware\Authorize::class,
62        'check-brand' => \App\Http\Middleware\CheckBrand::class,
63        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
64        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
65        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
66        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
67        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
68    ];
69}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/ApiCheckTimeStamps.php.html b/tests/coverage/Http/Middleware/ApiCheckTimeStamps.php.html new file mode 100644 index 0000000..ceee7fa --- /dev/null +++ b/tests/coverage/Http/Middleware/ApiCheckTimeStamps.php.html @@ -0,0 +1,194 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/ApiCheckTimeStamps.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
ApiCheckTimeStamps
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 handle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Enums\ApiError;
6use App\Exceptions\Api\V2\ApiErrorException;
7use App\Lib\Helper;
8use Illuminate\Http\Request;
9use Illuminate\Support\Carbon;
10
11/**
12 * Check Http Header Timestamp with request timestamp
13 * to prevent replay attacks
14 * https://restfulapi.net/security-essentials/
15 */
16class ApiCheckTimeStamps
17{
18    /**
19     * Handle an incoming request.
20     *
21     * @param  \Illuminate\Http\Request $request
22     * @param  \Closure                 $next
23     * @return mixed
24     * @throws ApiErrorException With ApiError::wrongTimeStamp if timestamp has an incorrect format or is timed out.
25     */
26    public function handle(Request $request, \Closure $next)
27    {
28        if (Helper::isRequestForApiV2($request)) {
29            if (!\is_string($request->timestamp) or !Carbon::hasFormat($request->timestamp, 'Y-m-d H:i:s')) {
30                throw new ApiErrorException(ApiError::wrongTimeStamp());
31            }
32            if (
33                Carbon::parse($request->timestamp, 'utc')
34                ->isBefore(Carbon::now('UTC')->subSeconds(config('api.timestampTimeout', 10)))
35            ) {
36                throw new ApiErrorException(ApiError::wrongTimeStamp());
37            }
38        }
39        return $next($request);
40    }
41}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/ApiResponse.php.html b/tests/coverage/Http/Middleware/ApiResponse.php.html new file mode 100644 index 0000000..18c928b --- /dev/null +++ b/tests/coverage/Http/Middleware/ApiResponse.php.html @@ -0,0 +1,226 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/ApiResponse.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
ApiResponse
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
10
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
 handle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
10
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Enums\ApiStatus;
6use App\Http\Resources\Api\V2\ApiResponse as V2ApiResponse;
7use App\Lib\Helper;
8use App\Models\Service;
9use Illuminate\Database\Eloquent\ModelNotFoundException;
10use Illuminate\Http\JsonResponse;
11use Illuminate\Http\Request;
12use Illuminate\Support\Facades\Log;
13
14class ApiResponse
15{
16    /**
17     * Handle an incoming request.
18     *
19     * @param  \Illuminate\Http\Request $request
20     * @param  \Closure                 $next
21     * @return mixed
22     * @throws \ErrorException If the received response is not a JsonResponse.
23     */
24    public function handle(Request $request, \Closure $next)
25    {
26        /** @var \Illuminate\Http\JsonResponse $response */
27        $response = $next($request);
28
29        if (\get_class($response) !== JsonResponse::class) {
30            // @codeCoverageIgnoreStart
31            throw new \ErrorException('All api responses should be json.');
32            // @codeCoverageIgnoreEnd
33        }
34
35        // Set no cache header.
36        $response->header('Cache-Control', 'No-Store');
37
38        if (Helper::isRequestForApiV1($request)) {
39            // @codeCoverageIgnoreStart
40            try {
41                $bearerToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token ?? '';
42                $service     = Service::getFromBearerToken($bearerToken);
43                $service->incrementStat();
44            } catch (ModelNotFoundException $e) {
45                Log::error(sprintf('Could not increment stats %s', $e->getMessage()));
46            }
47            return $response;
48            // @codeCoverageIgnoreEnd
49        }
50
51        /** @var \App\Models\Service|null */
52        if (\optional($service = app(Service::class))->id) {
53            $service->incrementStat();
54        }
55
56        /** @var object $data */
57        $data = $response->getData();
58
59        // If reponse is already a V2ApiResponse then just return it. This happens if error handler comes to action.
60        if (
61            isset($data->status) and
62            isset($data->data) and
63            isset($data->version) and
64            in_array($data->status, [ApiStatus::OK()->value, ApiStatus::KO()->value])
65        ) {
66            return $response;
67        }
68
69        /** @var \App\Enums\ApiStatus $status */
70        $status = intval($response->getStatusCode() / 100) == 2 ? ApiStatus::OK() : ApiStatus::KO();
71        return $response->setData(new V2ApiResponse((object)$response->getData(), $status));
72    }
73}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/Authenticate.php.html b/tests/coverage/Http/Middleware/Authenticate.php.html new file mode 100644 index 0000000..4624f66 --- /dev/null +++ b/tests/coverage/Http/Middleware/Authenticate.php.html @@ -0,0 +1,222 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/Authenticate.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
Authenticate
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 handle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 redirectTo
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Lib\Helper;
6use App\Models\Service;
7use Closure;
8use Illuminate\Auth\Middleware\Authenticate as Middleware;
9
10/**
11 * Updated for ApiV2
12 */
13class Authenticate extends Middleware
14{
15    /**
16     * Handle an incoming request.
17     *
18     * @param  mixed    $request
19     * @param  \Closure $next
20     * @param  mixed    ...$guards
21     * @return mixed
22     *
23     * @throws \Illuminate\Auth\AuthenticationException If authentification fails.
24     */
25    public function handle($request, Closure $next, ...$guards)
26    {
27        if (Helper::isRequestForApiV1($request)) {
28            // @codeCoverageIgnoreStart
29            // Use patched v1 Auth mecanism (without bearer reimplementation).
30            $bearerToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token ?? '';
31            $service     = Service::getFromBearerToken($bearerToken);
32            $request->request->add(['brand_id' => $service->brand->id]);
33            $response = $next($request);
34            // @codeCoverageIgnoreEnd
35        } else {
36            // Use Laravel auth check mecanism.
37            $response = \call_user_func_array(
38                [$this, 'parent::' . __FUNCTION__],
39                array_merge([$request, $next], $guards)
40            );
41        }
42
43        return $response;
44    }
45    /**
46     * Get the path the user should be redirected to when they are not authenticated.
47     *
48     * @param  mixed $request
49     * @return string|null
50     * @codeCoverageIgnore
51     */
52    protected function redirectTo($request)
53    {
54        if (! $request->expectsJson()) {
55            return route('login');
56        }
57    }
58}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/CheckBrand.php.html b/tests/coverage/Http/Middleware/CheckBrand.php.html new file mode 100644 index 0000000..b2a5945 --- /dev/null +++ b/tests/coverage/Http/Middleware/CheckBrand.php.html @@ -0,0 +1,153 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/CheckBrand.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CheckBrand
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 handle
n/a
0 / 0
3
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Lib\Helper;
6use App\Models\Service;
7use Closure;
8use Illuminate\Database\Eloquent\ModelNotFoundException;
9use Illuminate\Http\Request;
10
11/**
12 * @codeCoverageIgnore
13 */
14class CheckBrand
15{
16    /**
17     * Handle an incoming request.
18     *
19     * @param  \Illuminate\Http\Request $request
20     * @param  \Closure                 $next
21     * @return mixed
22     */
23    public function handle(Request $request, Closure $next)
24    {
25        $apiToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token ?? '';
26
27        if ($apiToken === null) {
28            abort(401, 'Le token d\'authentification est incorrect ' . \json_encode($request->all()));
29        }
30
31        try {
32            $brand = Service::getFromBearerToken($apiToken)->brand;
33            $request->request->add(['brand_id' => $brand->id]);
34        } catch (ModelNotFoundException $e) {
35            abort(401, 'Le token d\'authentification est incorrect ' . $e->getMessage());
36        }
37
38        return $next($request);
39    }
40}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/EncryptCookies.php.html b/tests/coverage/Http/Middleware/EncryptCookies.php.html new file mode 100644 index 0000000..4bf25ca --- /dev/null +++ b/tests/coverage/Http/Middleware/EncryptCookies.php.html @@ -0,0 +1,117 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/EncryptCookies.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
EncryptCookies
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
6
7class EncryptCookies extends Middleware
8{
9    /**
10     * The names of the cookies that should not be encrypted.
11     *
12     * @var array
13     */
14    protected $except = [];
15}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/JsonOnly.php.html b/tests/coverage/Http/Middleware/JsonOnly.php.html new file mode 100644 index 0000000..2e11ed6 --- /dev/null +++ b/tests/coverage/Http/Middleware/JsonOnly.php.html @@ -0,0 +1,184 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/JsonOnly.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
JsonOnly
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
 handle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Lib\Helper;
6use Illuminate\Http\Request;
7
8class JsonOnly
9{
10    /**
11     * Handle an incoming request.
12     *
13     * @param  \Illuminate\Http\Request $request
14     * @param  \Closure                 $next
15     * @return mixed
16     */
17    public function handle(Request $request, \Closure $next)
18    {
19        // TODO remove condition v1 check when dropped.
20        if (
21            !Helper::isRequestForApiV1($request) and
22            (!$request->isJson() or
23                !$request->expectsJson() or
24                is_null(json_decode($request->getContent())))
25        ) {
26            abort(400, 'Invalid JSON');
27        }
28
29        return $next($request);
30    }
31}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/PreventRequestsDuringMaintenance.php.html b/tests/coverage/Http/Middleware/PreventRequestsDuringMaintenance.php.html new file mode 100644 index 0000000..ade465d --- /dev/null +++ b/tests/coverage/Http/Middleware/PreventRequestsDuringMaintenance.php.html @@ -0,0 +1,117 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/PreventRequestsDuringMaintenance.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
PreventRequestsDuringMaintenance
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
6
7class PreventRequestsDuringMaintenance extends Middleware
8{
9    /**
10     * The URIs that should be reachable while maintenance mode is enabled.
11     *
12     * @var array
13     */
14    protected $except = [];
15}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/RedirectIfAuthenticated.php.html b/tests/coverage/Http/Middleware/RedirectIfAuthenticated.php.html new file mode 100644 index 0000000..6753c20 --- /dev/null +++ b/tests/coverage/Http/Middleware/RedirectIfAuthenticated.php.html @@ -0,0 +1,148 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/RedirectIfAuthenticated.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
RedirectIfAuthenticated
n/a
0 / 0
n/a
0 / 0
4
n/a
0 / 0
 handle
n/a
0 / 0
4
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Providers\RouteServiceProvider;
6use Closure;
7use Illuminate\Http\Request;
8use Illuminate\Support\Facades\Auth;
9
10/**
11 * @codeCoverageIgnore
12 */
13class RedirectIfAuthenticated
14{
15    /**
16     * Handle an incoming request.
17     *
18     * @param  \Illuminate\Http\Request $request
19     * @param  \Closure                 $next
20     * @param  string|null              ...$guards
21     * @return mixed
22     */
23    public function handle(Request $request, Closure $next, ...$guards)
24    {
25        $guards = empty($guards) ? [null] : $guards;
26
27        foreach ($guards as $guard) {
28            if (Auth::guard($guard)->check()) {
29                return redirect(RouteServiceProvider::HOME);
30            }
31        }
32
33        return $next($request);
34    }
35}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/TrimStrings.php.html b/tests/coverage/Http/Middleware/TrimStrings.php.html new file mode 100644 index 0000000..19c5447 --- /dev/null +++ b/tests/coverage/Http/Middleware/TrimStrings.php.html @@ -0,0 +1,120 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/TrimStrings.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
TrimStrings
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
6
7class TrimStrings extends Middleware
8{
9    /**
10     * The names of the attributes that should not be trimmed.
11     *
12     * @var array
13     */
14    protected $except = [
15        'password',
16        'password_confirmation',
17    ];
18}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/TrustHosts.php.html b/tests/coverage/Http/Middleware/TrustHosts.php.html new file mode 100644 index 0000000..553cf37 --- /dev/null +++ b/tests/coverage/Http/Middleware/TrustHosts.php.html @@ -0,0 +1,136 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/TrustHosts.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
TrustHosts
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 hosts
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Illuminate\Http\Middleware\TrustHosts as Middleware;
6
7/**
8 * @codeCoverageIgnore
9 */
10class TrustHosts extends Middleware
11{
12    /**
13     * Get the host patterns that should be trusted.
14     *
15     * @return array
16     */
17    public function hosts()
18    {
19        return [
20            $this->allSubdomainsOfApplicationUrl(),
21        ];
22    }
23}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/TrustProxies.php.html b/tests/coverage/Http/Middleware/TrustProxies.php.html new file mode 100644 index 0000000..d7b5be3 --- /dev/null +++ b/tests/coverage/Http/Middleware/TrustProxies.php.html @@ -0,0 +1,129 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/TrustProxies.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
TrustProxies
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Fideloper\Proxy\TrustProxies as Middleware;
6use Illuminate\Http\Request;
7
8class TrustProxies extends Middleware
9{
10    /**
11     * The trusted proxies for this application.
12     *
13     * @var array|string|null
14     */
15    protected $proxies;
16
17    /**
18     * The headers that should be used to detect proxies.
19     *
20     * @var integer
21     */
22    protected $headers = Request::HEADER_X_FORWARDED_FOR |
23    Request::HEADER_X_FORWARDED_HOST |
24    Request::HEADER_X_FORWARDED_PORT |
25    Request::HEADER_X_FORWARDED_PROTO |
26    Request::HEADER_X_FORWARDED_AWS_ELB;
27}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/VerifyCsrfToken.php.html b/tests/coverage/Http/Middleware/VerifyCsrfToken.php.html new file mode 100644 index 0000000..f060f8b --- /dev/null +++ b/tests/coverage/Http/Middleware/VerifyCsrfToken.php.html @@ -0,0 +1,117 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/VerifyCsrfToken.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
VerifyCsrfToken
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
6
7class VerifyCsrfToken extends Middleware
8{
9    /**
10     * The URIs that should be excluded from CSRF verification.
11     *
12     * @var array
13     */
14    protected $except = [];
15}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/XssSanitizer.php.html b/tests/coverage/Http/Middleware/XssSanitizer.php.html new file mode 100644 index 0000000..0de75ec --- /dev/null +++ b/tests/coverage/Http/Middleware/XssSanitizer.php.html @@ -0,0 +1,178 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware/XssSanitizer.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
XssSanitizer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
 handle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Closure;
6
7class XssSanitizer
8{
9    /**
10     * Handle an incoming request.
11     *
12     * @param  mixed    $request
13     * @param  \Closure $next
14     * @return mixed
15     */
16    public function handle($request, Closure $next)
17    {
18        $input = $request->all();
19        array_walk_recursive($input, function (&$input) {
20            $input = strip_tags($input);
21        });
22        $request->merge($input);
23        return $next($request);
24    }
25}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/dashboard.html b/tests/coverage/Http/Middleware/dashboard.html new file mode 100644 index 0000000..9b8263b --- /dev/null +++ b/tests/coverage/Http/Middleware/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Middleware + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Middleware/index.html b/tests/coverage/Http/Middleware/index.html new file mode 100644 index 0000000..d2422bd --- /dev/null +++ b/tests/coverage/Http/Middleware/index.html @@ -0,0 +1,336 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Middleware + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
37 / 37
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
ApiCheckTimeStamps.php
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiResponse.php
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Authenticate.php
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CheckBrand.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
EncryptCookies.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
JsonOnly.php
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
PreventRequestsDuringMaintenance.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
RedirectIfAuthenticated.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
TrimStrings.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
TrustHosts.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
TrustProxies.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
VerifyCsrfToken.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
XssSanitizer.php
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/Http/Requests/Api/V2/AuthenticateRequest.php.html b/tests/coverage/Http/Requests/Api/V2/AuthenticateRequest.php.html new file mode 100644 index 0000000..6c05540 --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/AuthenticateRequest.php.html @@ -0,0 +1,205 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api/V2/AuthenticateRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
AuthenticateRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 authorize
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5class AuthenticateRequest extends BaseApiRequest
6{
7    /**
8     * Determine if the user is authorized to make this request.
9     *
10     * @return boolean
11     */
12    public function authorize()
13    {
14        return true;
15    }
16
17    /**
18     * Get the validation rules that apply to the request.
19     *
20     * @return array
21     */
22    public function rules(): array
23    {
24        return [
25            'email' => 'required|email:rfc',
26            'password' => 'required'
27        ];
28    }
29}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/V2/BaseApiRequest.php.html b/tests/coverage/Http/Requests/Api/V2/BaseApiRequest.php.html new file mode 100644 index 0000000..4e91307 --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/BaseApiRequest.php.html @@ -0,0 +1,216 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api/V2/BaseApiRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
BaseApiRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 authorize
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 rules
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Http\Sanitizers\RemoveSpaces;
6use ArondeParon\RequestSanitizer\Sanitizers\Capitalize;
7use ArondeParon\RequestSanitizer\Sanitizers\Lowercase;
8use ArondeParon\RequestSanitizer\Sanitizers\RemoveNonNumeric;
9use ArondeParon\RequestSanitizer\Sanitizers\Trim;
10use ArondeParon\RequestSanitizer\Sanitizers\TrimDuplicateSpaces;
11use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
12use Illuminate\Foundation\Http\FormRequest;
13
14abstract class BaseApiRequest extends FormRequest
15{
16    use SanitizesInputs;
17
18    /**
19     * Using these sanitizers before proceeding the request
20     * @var array
21     */
22    protected $sanitizers = [
23        'firstname' => [Trim::class, TrimDuplicateSpaces::class, Capitalize::class],
24        'lastname' => [Trim::class, TrimDuplicateSpaces::class, Capitalize::class],
25        'address' => [Trim::class, TrimDuplicateSpaces::class],
26        'address2' => [Trim::class, TrimDuplicateSpaces::class],
27        'city' => [Trim::class, TrimDuplicateSpaces::class],
28        'password' => Trim::class,
29        'phone' => [Trim::class, RemoveSpaces::class],
30        'email' => [Lowercase::class, Trim::class, RemoveSpaces::class],
31        'postcode' => [RemoveNonNumeric::class, Trim::class]
32    ];
33
34    /**
35     * Determine if the user is authorized to make this request.
36     *
37     * @return boolean
38     */
39    public function authorize()
40    {
41        return auth()->check();
42    }
43
44    /**
45     * Get the validation rules that apply to the request.
46     *
47     * @return array
48     */
49    abstract public function rules(): array;
50}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/V2/GetBrandStatsRequest.php.html b/tests/coverage/Http/Requests/Api/V2/GetBrandStatsRequest.php.html new file mode 100644 index 0000000..9aeff49 --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/GetBrandStatsRequest.php.html @@ -0,0 +1,194 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api/V2/GetBrandStatsRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
GetBrandStatsRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Http\Requests\Api\V2\BaseApiRequest;
6use Illuminate\Support\Carbon;
7use Illuminate\Validation\ValidationException;
8
9class GetBrandStatsRequest extends BaseApiRequest
10{
11    /**
12     * Get the validation rules that apply to the request.
13     *
14     * @return array
15     * @throws ValidationException If given dates are unparsable of period is bigger than 3 months.
16     * @throws \Exception If carbon date processing failed.
17     */
18    public function rules(): array
19    {
20        try {
21            if (Carbon::parse($this->end)->diffInMonths(Carbon::parse($this->start)->addDay()) >= 3) {
22                throw ValidationException::withMessages([
23                    'Vous devez sélectionner une période de maximum 3 mois'
24                ]);
25            }
26        } catch (\Exception $e) {
27            if (\get_class($e) !== ValidationException::class) {
28                throw ValidationException::withMessages([
29                    'Les dates séléectionnées ne sembles pas être valides'
30                ]);
31            }
32            throw $e;
33        }
34        return [
35            'start' => 'required|date|date_format:Y-m-d',
36            'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
37        ];
38    }
39}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/V2/GetQuestionStatsRequest.php.html b/tests/coverage/Http/Requests/Api/V2/GetQuestionStatsRequest.php.html new file mode 100644 index 0000000..7da111d --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/GetQuestionStatsRequest.php.html @@ -0,0 +1,195 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api/V2/GetQuestionStatsRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
GetQuestionStatsRequest
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
4.18
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
 rules
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
4.18
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Http\Requests\Api\V2\BaseApiRequest;
6use Exception;
7use Illuminate\Support\Carbon;
8use Illuminate\Validation\ValidationException;
9
10class GetQuestionStatsRequest extends BaseApiRequest
11{
12    /**
13     * Get the validation rules that apply to the request.
14     *
15     * @return array
16     * @throws ValidationException If given dates are unparsable of period is bigger than 3 months.
17     * @throws \Exception If carbon date processing failed.
18     */
19    public function rules(): array
20    {
21        try {
22            if (Carbon::parse($this->end)->diffInMonths(Carbon::parse($this->start)->addDay()) >= 3) {
23                throw ValidationException::withMessages([
24                    'Vous devez sélectionner une période de maximum 3 mois'
25                ]);
26            }
27        } catch (Exception $e) {
28            if (\get_class($e) !== ValidationException::class) {
29                throw ValidationException::withMessages([
30                    'Les dates sélectionnées ne sembles pas être valides'
31                ]);
32            }
33            throw $e;
34        }
35        return [
36            'start' => 'required|date|date_format:Y-m-d',
37            'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
38        ];
39    }
40}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php.html b/tests/coverage/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php.html new file mode 100644 index 0000000..7a9ab4f --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php.html @@ -0,0 +1,304 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
35 / 35
SendArchadiaDefaultMailRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
35 / 35
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
17 / 17
 attributes
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
16 / 16
 messages
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Enums\ArcadiaOrderType;
6use ArondeParon\RequestSanitizer\Sanitizers\RemoveNonNumeric;
7use ArondeParon\RequestSanitizer\Sanitizers\Trim;
8use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
9use Kwaadpepper\Enum\Rules\EnumIsValidRule;
10
11class SendArchadiaDefaultMailRequest extends BaseApiRequest
12{
13    use SanitizesInputs;
14
15    /**
16     * Using these sanitizers before proceeding the request
17     * @var array
18     */
19    protected $sanitizers = [
20        'label_type_support' => Trim::class,
21        'operation' => Trim::class,
22        'order_date' => Trim::class,
23        'order_num' => RemoveNonNumeric::class,
24        'package_unit_weight' => RemoveNonNumeric::class,
25        'support_code' => Trim::class,
26        'deliver.firstname' => Trim::class,
27        'deliver.lastname' => Trim::class,
28        'deliver.email' => Trim::class,
29        'deliver.address' => Trim::class,
30        'deliver.address2' => Trim::class,
31        'deliver.city' => Trim::class,
32        'deliver.postcode' => Trim::class,
33        'deliver.phone' => Trim::class
34    ];
35
36    /**
37     * Get the validation rules that apply to the request.
38     *
39     * @return array
40     */
41    public function rules(): array
42    {
43        return [
44            'label_type_support' => 'required|string',
45            'operation' => 'required|int',
46            'order_date' => 'required|date',
47            'order_num' => 'required|numeric',
48            'package_unit_weight' => 'required|integer',
49            'state_order' => [
50                'required',
51                new EnumIsValidRule(ArcadiaOrderType::class)
52            ],
53            'support_code' => 'required|int',
54            'deliver' => 'required|array',
55            'deliver.firstname' => 'required|string|min:1|max:255',
56            'deliver.lastname' => 'required|string|min:1|max:255',
57            'deliver.email' => 'required|email:rfc,dns,spoof,filter|max:255',
58            'deliver.address' => 'required|string|min:2|max:255',
59            'deliver.address2' => 'sometimes|string|min:2|max:255',
60            'deliver.city' => 'required|string|min:2|max:255',
61            'deliver.postcode' => 'required|string|postal_code:FR|max:255',
62            'deliver.phone' => 'required|string|phone:FR|max:255',
63        ];
64    }
65
66    /**
67     * Attributes
68     *
69     * @return array
70     */
71    public function attributes()
72    {
73        return [
74            'label_type_support' => trans('label type support'),
75            'operation' => trans('code opération'),
76            'order_date' => trans('date de la commande'),
77            'order_num' => trans('numero de commande'),
78            'package_unit_weight' => trans('poids unitaire du paquet'),
79            'state_order' => trans('type de commande arcadia'),
80            'support_code' => trans('code de support'),
81            'deliver' => trans('coordonées de livraison'),
82            'deliver.civility' => trans('civilitée dans coordonées de livraison'),
83            'deliver.firstname' => trans('prénom dans coordonées de livraison'),
84            'deliver.lastname' => trans('nom dans coordonées de livraison'),
85            'deliver.address' => trans('adresse dans coordonées de livraison'),
86            'deliver.postcode' => trans('code postal dans coordonées de livraison'),
87            'deliver.phone' => trans('téléphone dans coordonées de livraison'),
88            'deliver.city' => trans('ville dans coordonées de livraison'),
89            'deliver.email' => trans('e-mail dans coordonées de livraison'),
90        ];
91    }
92
93    /**
94     * Messages
95     *
96     * @return array
97     */
98    public function messages()
99    {
100        return [
101            // phpcs:ignore Generic.Files.LineLength.TooLong
102            'deliver.phone.phone' => trans('Le champ :attribute est invalide, il accepte les numéros des pays suivants: France.'),
103            // phpcs:ignore Generic.Files.LineLength.TooLong
104            'deliver.postcode.postal_code' => trans('Le champ :attribute accepte les codes postaux des pays suivants :countries comme par exemple :examples.'),
105        ];
106    }
107}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php.html b/tests/coverage/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php.html new file mode 100644 index 0000000..07ec2e7 --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php.html @@ -0,0 +1,297 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
SendArchadiaGameMailRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 attributes
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
 messages
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use ArondeParon\RequestSanitizer\Sanitizers\Trim;
6use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
7
8class SendArchadiaGameMailRequest extends BaseApiRequest
9{
10    use SanitizesInputs;
11
12    /**
13     * Using these sanitizers before proceeding the request
14     * @var array
15     */
16    protected $sanitizers = [
17        'code_on_pack' => Trim::class,
18        'code_unique' => Trim::class,
19        'label_type_support' => Trim::class,
20        'operation' => Trim::class,
21        'order_date' => Trim::class,
22        'support_code' => Trim::class,
23        'type' => Trim::class,
24        'deliver.firstname' => Trim::class,
25        'deliver.lastname' => Trim::class,
26        'deliver.email' => Trim::class,
27        'deliver.address' => Trim::class,
28        'deliver.address2' => Trim::class,
29        'deliver.city' => Trim::class,
30        'deliver.postcode' => Trim::class,
31        'deliver.phone' => Trim::class
32    ];
33
34    /**
35     * Get the validation rules that apply to the request.
36     *
37     * @return array
38     */
39    public function rules(): array
40    {
41        return [
42            'code_on_pack' => 'sometimes|string',
43            'code_unique' => 'required|string',
44            'label_type_support' => 'required|string',
45            'operation' => 'required|int',
46            'order_date' => 'required|date',
47            'support_code' => 'required|int',
48            'deliver' => 'required|array',
49            'deliver.firstname' => 'required|string|min:1|max:255',
50            'deliver.lastname' => 'required|string|min:1|max:255',
51            'deliver.email' => 'required|email:rfc,dns,spoof,filter|max:255',
52            'deliver.address' => 'required|string|min:2|max:255',
53            'deliver.address2' => 'sometimes|string|min:2|max:255',
54            'deliver.city' => 'required|string|min:2|max:255',
55            'deliver.postcode' => 'required|string|postal_code:FR|max:255',
56            'deliver.phone' => 'required|string|phone:FR|max:255',
57        ];
58    }
59
60    /**
61     * Attributes
62     *
63     * @return array
64     */
65    public function attributes()
66    {
67        return [
68            'code_on_pack' => trans('code ean emballage'),
69            'code_unique' => trans('code unique'),
70            'label_type_support' => trans('label type support'),
71            'operation' => trans('code opération'),
72            'order_date' => trans('date de la commande'),
73            'support_code' => trans('code de support'),
74            'deliver' => trans('coordonées de livraison'),
75            'deliver.civility' => trans('civilitée dans coordonées de livraison'),
76            'deliver.firstname' => trans('prénom dans coordonées de livraison'),
77            'deliver.lastname' => trans('nom dans coordonées de livraison'),
78            'deliver.address' => trans('adresse dans coordonées de livraison'),
79            'deliver.postcode' => trans('code postal dans coordonées de livraison'),
80            'deliver.phone' => trans('téléphone dans coordonées de livraison'),
81            'deliver.city' => trans('ville dans coordonées de livraison'),
82            'deliver.email' => trans('e-mail dans coordonées de livraison'),
83        ];
84    }
85
86    /**
87     * Messages
88     *
89     * @return array
90     */
91    public function messages()
92    {
93        return [
94            // phpcs:ignore Generic.Files.LineLength.TooLong
95            'deliver.phone.phone' => trans('Le champ :attribute est invalide, il accepte les numéros des pays suivants: France.'),
96            // phpcs:ignore Generic.Files.LineLength.TooLong
97            'deliver.postcode.postal_code' => trans('Le champ :attribute accepte les codes postaux des pays suivants :countries comme par exemple :examples.'),
98        ];
99    }
100}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/V2/SingleOptinRequest.php.html b/tests/coverage/Http/Requests/Api/V2/SingleOptinRequest.php.html new file mode 100644 index 0000000..0171776 --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/SingleOptinRequest.php.html @@ -0,0 +1,174 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api/V2/SingleOptinRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
SingleOptinRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5class SingleOptinRequest extends BaseApiRequest
6{
7    /**
8     * Get the validation rules that apply to the request.
9     *
10     * @return array
11     */
12    public function rules(): array
13    {
14        return [
15            'email' => 'required|email:rfc,dns,spoof,filter',
16            'opt_in' => 'required|boolean'
17        ];
18    }
19}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/V2/StoreCustomerAnswers.php.html b/tests/coverage/Http/Requests/Api/V2/StoreCustomerAnswers.php.html new file mode 100644 index 0000000..b937577 --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/StoreCustomerAnswers.php.html @@ -0,0 +1,202 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api/V2/StoreCustomerAnswers.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 89.47% covered (warning) +
+
+
89.47%
17 / 19
StoreCustomerAnswers
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.01
+
+ 89.47% covered (warning) +
+
+
89.47%
17 / 19
 rules
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.01
+
+ 89.47% covered (warning) +
+
+
89.47%
17 / 19
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Models\Answer;
6use App\Models\Question;
7
8class StoreCustomerAnswers extends BaseApiRequest
9{
10    /**
11     * Get the validation rules that apply to the request.
12     *
13     * @return array
14     */
15    public function rules(): array
16    {
17        // * Resolve Service.
18        $service           = app()->make(\App\Models\Service::class);
19        $brandId           = $service->brand->id;
20        $question          = Question::findOrFail($this->question);
21        $questionTableName = (new Question())->getTable();
22        $answerTableName   = (new Answer())->getTable();
23        return [
24            'question' => [
25                'required',
26                "exists:$questionTableName,id",
27                // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
28                function ($attribute, $value, $fail) use ($question, $brandId) {
29                    if ($question->brand->id !== $brandId) {
30                        $fail("$attribute does not belong to brand $brandId");
31                    }
32                }
33            ],
34            'answers' => 'required|array',
35            'answers.*' => [
36                'int',
37                'distinct',
38                "exists:$answerTableName,id",
39                function ($attribute, $value, $fail) use ($question) {
40                    if (Answer::findOrFail($value)->question->id !== $question->id) {
41                        $fail("$attribute does not belong to question $question->id");
42                    }
43                }
44            ]
45        ];
46    }
47}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/V2/StoreCustomerRequest.php.html b/tests/coverage/Http/Requests/Api/V2/StoreCustomerRequest.php.html new file mode 100644 index 0000000..d35ffcd --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/StoreCustomerRequest.php.html @@ -0,0 +1,257 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api/V2/StoreCustomerRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
40 / 40
StoreCustomerRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
40 / 40
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
38 / 38
 messages
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Enums\Country;
6use Illuminate\Validation\Rules\Password;
7use Kwaadpepper\Enum\Rules\EnumIsValidRule;
8
9class StoreCustomerRequest extends BaseApiRequest
10{
11    /**
12     * Get the validation rules that apply to the request.
13     *
14     * @return array
15     */
16    public function rules(): array
17    {
18        return [
19            'firstname' => 'required|string|min:1|max:255',
20            'lastname' => 'required|string|min:1|max:255',
21            'email' => 'required|email:rfc,dns,spoof,filter|max:255',
22            'password' => [
23                'required',
24                'max:255',
25                Password::min(8)
26                    ->mixedCase()
27                    ->letters()
28                    ->numbers()
29                    ->symbols()
30                    ->uncompromised()
31            ],
32            'birth_date' => 'required|date_format:Y-m-d|max:255',
33            'address' => 'sometimes|required_with:address2|string|max:255',
34            'address2' => 'sometimes|string|max:255',
35            'phone' => 'sometimes|string|phone:FR,BE,LU',
36            'country' => [
37                'sometimes',
38                'numeric',
39                new EnumIsValidRule(Country::class),
40                sprintf(
41                    'in:%s',
42                    implode(',', [
43                        Country::belgium()->value,
44                        Country::luxembourg()->value,
45                        Country::france()->value,
46                        // * DROM COM.
47                        Country::guadeloupe()->value,
48                        Country::martinique()->value,
49                        Country::frenchGuiana()->value,
50                        Country::reunion()->value,
51                        Country::saintPierreAndMiquelon()->value,
52                        Country::mayotte()->value,
53                        Country::saintBarthelemy()->value,
54                        Country::saintMartinFrenchPart()->value,
55                        Country::wallisAndFutuna()->value,
56                        Country::frenchPolynesia()->value,
57                        Country::newCaledonia()->value
58                    ])
59                )
60            ],
61            'city' => 'sometimes|string|min:2|max:255',
62            'postcode' => 'sometimes|string|postal_code:FR,BE,LU',
63            'opt_in' => 'required|boolean',
64        ];
65    }
66
67    /**
68     * Messages
69     *
70     * @return array
71     */
72    public function messages()
73    {
74        return [
75            // phpcs:ignore Generic.Files.LineLength.TooLong
76            'phone.phone' => trans('Le champ :attribute est invalide, il accepte les numéros des pays suivants France, Belgique, Luxembourg.'),
77            // phpcs:ignore Generic.Files.LineLength.TooLong
78            'country.in' => trans('Le champ :attribute est invalide, il accepte les pays suivants France, Belgique, Luxembourg.')
79        ];
80    }
81}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/V2/UpdateCustomerRequest.php.html b/tests/coverage/Http/Requests/Api/V2/UpdateCustomerRequest.php.html new file mode 100644 index 0000000..d747194 --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/UpdateCustomerRequest.php.html @@ -0,0 +1,266 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api/V2/UpdateCustomerRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
44 / 44
UpdateCustomerRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
44 / 44
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
42 / 42
 messages
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Enums\Country;
6use Illuminate\Validation\Rules\Password;
7use Kwaadpepper\Enum\Rules\EnumIsValidRule;
8
9class UpdateCustomerRequest extends BaseApiRequest
10{
11    /**
12     * Get the validation rules that apply to the request.
13     *
14     * @return array
15     */
16    public function rules(): array
17    {
18        $fields  = [
19            'firstname', 'lastname', 'email', 'password',
20            'birth_date', 'address', 'phone', 'country',
21            'city', 'postcode', 'opt_in'
22        ];
23        $atLeast = function ($attribute) use ($fields) {
24            return 'required_without_all:' . collect($fields)
25                ->filter(function ($fieldName) use ($attribute) {
26                    return $fieldName !== $attribute;
27                })->implode(',');
28        };
29        return [
30            'firstname' => [$atLeast('firstname'), 'string', 'min:1', 'max:255'],
31            'lastname' => [$atLeast('lastname'), 'string', 'min:1', 'max:255'],
32            'email' => [$atLeast('email'), 'email:rfc,dns,spoof,filter', 'max:255'],
33            'password' => [
34                $atLeast('password'), 'max:255', Password::min(8)
35                    ->mixedCase()
36                    ->letters()
37                    ->numbers()
38                    ->symbols()
39                    ->uncompromised()
40            ],
41            'birth_date' => [$atLeast('birth_date'), 'date_format:Y-m-d', 'max:255'],
42            'address' => [$atLeast('address'), 'required_with:address2', 'string', 'max:255'],
43            'address2' => ['sometimes', 'string', 'max:255'],
44            'phone' => [$atLeast('phone'), 'string', 'phone:FR,BE,LU', 'max:255'],
45            'country' => [
46                $atLeast('country'),
47                'numeric',
48                new EnumIsValidRule(Country::class),
49                sprintf(
50                    'in:%s',
51                    implode(',', [
52                        Country::belgium()->value,
53                        Country::luxembourg()->value,
54                        Country::france()->value,
55                        // * DROM COM.
56                        Country::guadeloupe()->value,
57                        Country::martinique()->value,
58                        Country::frenchGuiana()->value,
59                        Country::reunion()->value,
60                        Country::saintPierreAndMiquelon()->value,
61                        Country::mayotte()->value,
62                        Country::saintBarthelemy()->value,
63                        Country::saintMartinFrenchPart()->value,
64                        Country::wallisAndFutuna()->value,
65                        Country::frenchPolynesia()->value,
66                        Country::newCaledonia()->value
67                    ])
68                )
69            ],
70            'city' => [$atLeast('city'), 'string', 'min:2', 'max:255'],
71            'postcode' => [$atLeast('postcode'), 'string', 'postal_code:FR,BE,LU', 'max:255'],
72            'opt_in' => [$atLeast('opt_in'), 'boolean'],
73        ];
74    }
75
76    /**
77     * Messages
78     *
79     * @return array
80     */
81    public function messages()
82    {
83        return [
84            // phpcs:ignore Generic.Files.LineLength.TooLong
85            'phone.phone' => trans('Le champ :attribute est invalide, il accepte les numéros des pays suivants France, Belgique, Luxembourg.'),
86            // phpcs:ignore Generic.Files.LineLength.TooLong
87            'country.in' => trans('Le champ :attribute est invalide, il accepte les pays suivants France, Belgique, Luxembourg.')
88        ];
89    }
90}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/V2/dashboard.html b/tests/coverage/Http/Requests/Api/V2/dashboard.html new file mode 100644 index 0000000..6b0a058 --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/dashboard.html @@ -0,0 +1,295 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Requests/Api/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+ +
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
rules77%
rules89%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + +
MethodCRAP
rules4
rules3
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/V2/index.html b/tests/coverage/Http/Requests/Api/V2/index.html new file mode 100644 index 0000000..5c76b28 --- /dev/null +++ b/tests/coverage/Http/Requests/Api/V2/index.html @@ -0,0 +1,374 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
AuthenticateRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
BaseApiRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
GetBrandStatsRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
GetQuestionStatsRequest.php
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
SendArchadiaDefaultMailRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
35 / 35
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
SendArchadiaGameMailRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
SingleOptinRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
StoreCustomerAnswers.php
+
+ 89.47% covered (warning) +
+
+
89.47%
17 / 19
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
StoreCustomerRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
40 / 40
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
UpdateCustomerRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
44 / 44
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/Http/Requests/Api/dashboard.html b/tests/coverage/Http/Requests/Api/dashboard.html new file mode 100644 index 0000000..d72206e --- /dev/null +++ b/tests/coverage/Http/Requests/Api/dashboard.html @@ -0,0 +1,294 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Requests/Api + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+ +
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
rules77%
rules89%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + +
MethodCRAP
rules4
rules3
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Requests/Api/index.html b/tests/coverage/Http/Requests/Api/index.html new file mode 100644 index 0000000..ffcc940 --- /dev/null +++ b/tests/coverage/Http/Requests/Api/index.html @@ -0,0 +1,121 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/Api + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
V2
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ +
+ + diff --git a/tests/coverage/Http/Requests/CustomerExport.php.html b/tests/coverage/Http/Requests/CustomerExport.php.html new file mode 100644 index 0000000..8aa6d2d --- /dev/null +++ b/tests/coverage/Http/Requests/CustomerExport.php.html @@ -0,0 +1,168 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/CustomerExport.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerExport
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use App\Enums\CustomerExportType;
6use Illuminate\Foundation\Http\FormRequest;
7
8/**
9 * @codeCoverageIgnore
10 */
11class CustomerExport extends FormRequest
12{
13    /**
14     * Determine if the user is authorized to make this request.
15     *
16     * @return boolean
17     */
18    public function authorize()
19    {
20        return auth()->check();
21    }
22
23    /**
24     * Get the validation rules that apply to the request.
25     *
26     * @return array
27     */
28    public function rules()
29    {
30        return [
31            'brand' => 'required|numeric|exists:brands,id',
32            'export_type' => [
33                'required',
34                'numeric',
35                // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
36                function ($attribute, $value, $fail) {
37                    if (!\in_array($value, CustomerExportType::values())) {
38                        $fail(trans('Le type d\'export invalide'));
39                    }
40                }
41            ]
42        ];
43    }
44}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/GenerateServiceToken.php.html b/tests/coverage/Http/Requests/GenerateServiceToken.php.html new file mode 100644 index 0000000..8965ed5 --- /dev/null +++ b/tests/coverage/Http/Requests/GenerateServiceToken.php.html @@ -0,0 +1,155 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/GenerateServiceToken.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
GenerateServiceToken
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use Illuminate\Foundation\Http\FormRequest;
6
7/**
8 * @codeCoverageIgnore
9 */
10class GenerateServiceToken extends FormRequest
11{
12    /**
13     * Determine if the user is authorized to make this request.
14     *
15     * @return boolean
16     */
17    public function authorize()
18    {
19        return auth()->check();
20    }
21
22    /**
23     * Get the validation rules that apply to the request.
24     *
25     * @return array
26     */
27    public function rules()
28    {
29        return [];
30    }
31}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/ManageBrandRequest.php.html b/tests/coverage/Http/Requests/ManageBrandRequest.php.html new file mode 100644 index 0000000..2215ef7 --- /dev/null +++ b/tests/coverage/Http/Requests/ManageBrandRequest.php.html @@ -0,0 +1,176 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/ManageBrandRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ManageBrandRequest
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use ArondeParon\RequestSanitizer\Sanitizers\FilterVars;
6use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
7use Illuminate\Foundation\Http\FormRequest;
8
9/**
10 * @codeCoverageIgnore
11 */
12class ManageBrandRequest extends FormRequest
13{
14    use SanitizesInputs;
15
16    /**
17     * Using these sanitizers before proceeding the request
18     * @var array
19     */
20    protected $sanitizers = [
21        'name' => [
22            FilterVars::class => [
23                'filter' => FILTER_SANITIZE_STRING
24            ]
25        ],
26    ];
27
28    /**
29     * Determine if the user is authorized to make this request.
30     *
31     * @return boolean
32     */
33    public function authorize()
34    {
35        return auth()->check();
36    }
37
38    /**
39     * Get the validation rules that apply to the request.
40     *
41     * @return array
42     */
43    public function rules()
44    {
45        if ($this->isMethod('post')) {
46            return [
47                'name' => 'required|string|min:5|max:255'
48            ];
49        }
50        return [];
51    }
52}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/ManageServiceRequest.php.html b/tests/coverage/Http/Requests/ManageServiceRequest.php.html new file mode 100644 index 0000000..dd09ab7 --- /dev/null +++ b/tests/coverage/Http/Requests/ManageServiceRequest.php.html @@ -0,0 +1,177 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/ManageServiceRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ManageServiceRequest
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use ArondeParon\RequestSanitizer\Sanitizers\FilterVars;
6use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
7use Illuminate\Foundation\Http\FormRequest;
8
9/**
10 * @codeCoverageIgnore
11 */
12class ManageServiceRequest extends FormRequest
13{
14    use SanitizesInputs;
15
16    /**
17     * Using these sanitizers before proceeding the request
18     * @var array
19     */
20    protected $sanitizers = [
21        'name' => [
22            FilterVars::class => [
23                'filter' => FILTER_SANITIZE_STRING
24            ]
25        ],
26    ];
27
28    /**
29     * Determine if the user is authorized to make this request.
30     *
31     * @return boolean
32     */
33    public function authorize()
34    {
35        return auth()->check();
36    }
37
38    /**
39     * Get the validation rules that apply to the request.
40     *
41     * @return array
42     */
43    public function rules()
44    {
45        if ($this->isMethod('post')) {
46            return [
47                'name' => 'required|string|min:5|max:255',
48                'brand_id' => 'required|exists:brands,id'
49            ];
50        }
51        return [];
52    }
53}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/QuestionExport.php.html b/tests/coverage/Http/Requests/QuestionExport.php.html new file mode 100644 index 0000000..f86d6ef --- /dev/null +++ b/tests/coverage/Http/Requests/QuestionExport.php.html @@ -0,0 +1,157 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/QuestionExport.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
QuestionExport
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use Illuminate\Foundation\Http\FormRequest;
6
7/**
8 * @codeCoverageIgnore
9 */
10class QuestionExport extends FormRequest
11{
12    /**
13     * Determine if the user is authorized to make this request.
14     *
15     * @return boolean
16     */
17    public function authorize()
18    {
19        return auth()->check();
20    }
21
22    /**
23     * Get the validation rules that apply to the request.
24     *
25     * @return array
26     */
27    public function rules()
28    {
29        return [
30            'brand' => 'required|numeric|exists:brands,id'
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/StatsByBrandRequest.php.html b/tests/coverage/Http/Requests/StatsByBrandRequest.php.html new file mode 100644 index 0000000..4f31206 --- /dev/null +++ b/tests/coverage/Http/Requests/StatsByBrandRequest.php.html @@ -0,0 +1,181 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/StatsByBrandRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StatsByBrandRequest
n/a
0 / 0
n/a
0 / 0
6
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
5
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use Exception;
6use Illuminate\Foundation\Http\FormRequest;
7use Illuminate\Support\Carbon;
8use Illuminate\Validation\ValidationException;
9
10/**
11 * @codeCoverageIgnore
12 */
13class StatsByBrandRequest extends FormRequest
14{
15    /**
16     * Determine if the user is authorized to make this request.
17     *
18     * @return boolean
19     */
20    public function authorize()
21    {
22        return auth()->check();
23    }
24
25    /**
26     * Get the validation rules that apply to the request.
27     *
28     * @return array
29     * @throws ValidationException If given dates are unparsable of period is bigger than 1 year.
30     * @throws \Exception If carbon date processing failed.
31     */
32    public function rules()
33    {
34        if ($this->post()) {
35            try {
36                if ((Carbon::parse($this->end)->diffInDays(Carbon::parse($this->start)) + 1) > 365) {
37                    throw ValidationException::withMessages([
38                        'Vous devez sélectionner une période de maximum 1 ans'
39                    ]);
40                }
41            } catch (Exception $e) {
42                if (\get_class($e) !== ValidationException::class) {
43                    throw ValidationException::withMessages([
44                        'Les dates séléectionnées ne sembles pas être valides'
45                    ]);
46                }
47                throw $e;
48            }
49            return [
50                'brand' => 'required|exists:brands,id',
51                'start' => 'required|date|date_format:Y-m-d',
52                'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
53            ];
54        }//end if
55        return [];
56    }
57}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/StatsByQuestionRequest.php.html b/tests/coverage/Http/Requests/StatsByQuestionRequest.php.html new file mode 100644 index 0000000..3388c92 --- /dev/null +++ b/tests/coverage/Http/Requests/StatsByQuestionRequest.php.html @@ -0,0 +1,181 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests/StatsByQuestionRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StatsByQuestionRequest
n/a
0 / 0
n/a
0 / 0
6
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
5
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use Exception;
6use Illuminate\Foundation\Http\FormRequest;
7use Illuminate\Support\Carbon;
8use Illuminate\Validation\ValidationException;
9
10/**
11 * @codeCoverageIgnore
12 */
13class StatsByQuestionRequest extends FormRequest
14{
15    /**
16     * Determine if the user is authorized to make this request.
17     *
18     * @return boolean
19     */
20    public function authorize()
21    {
22        return auth()->check();
23    }
24
25    /**
26     * Get the validation rules that apply to the request.
27     *
28     * @return array
29     * @throws ValidationException If given dates are unparsable of period is bigger than 1 year.
30     * @throws \Exception If carbon date processing failed.
31     */
32    public function rules()
33    {
34        if ($this->post()) {
35            try {
36                if ((Carbon::parse($this->end)->diffInDays(Carbon::parse($this->start)) + 1) > 365) {
37                    throw ValidationException::withMessages([
38                        'Vous devez sélectionner une période de maximum 1 ans'
39                    ]);
40                }
41            } catch (Exception $e) {
42                if (\get_class($e) !== ValidationException::class) {
43                    throw ValidationException::withMessages([
44                        'Les dates séléectionnées ne sembles pas être valides'
45                    ]);
46                }
47                throw $e;
48            }
49            return [
50                'question' => 'required|exists:questions,id',
51                'start' => 'required|date|date_format:Y-m-d',
52                'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
53            ];
54        }//end if
55        return [];
56    }
57}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Requests/dashboard.html b/tests/coverage/Http/Requests/dashboard.html new file mode 100644 index 0000000..f87303d --- /dev/null +++ b/tests/coverage/Http/Requests/dashboard.html @@ -0,0 +1,293 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Requests + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+ +
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
rules77%
rules89%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + +
MethodCRAP
rules4
rules3
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Requests/index.html b/tests/coverage/Http/Requests/index.html new file mode 100644 index 0000000..d04dc67 --- /dev/null +++ b/tests/coverage/Http/Requests/index.html @@ -0,0 +1,211 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Requests + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
Api
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
CustomerExport.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
GenerateServiceToken.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ManageBrandRequest.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ManageServiceRequest.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
QuestionExport.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StatsByBrandRequest.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StatsByQuestionRequest.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Http/Resources/Api/V2/AnswersResource.php.html b/tests/coverage/Http/Resources/Api/V2/AnswersResource.php.html new file mode 100644 index 0000000..1ed3545 --- /dev/null +++ b/tests/coverage/Http/Resources/Api/V2/AnswersResource.php.html @@ -0,0 +1,181 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Resources/Api/V2/AnswersResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
AnswersResource
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Models\Answer;
6use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
7
8class AnswersResource extends JsonResourceCollection
9{
10    /**
11     * Transform the resource into an array.
12     *
13     * @param  mixed  $request
14     * @return array
15     */
16    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
17    public function toArray($request)
18    {
19        return $this->collection->map(function (Answer $answer) {
20            return [
21                'id' => $answer->id,
22                'answer' => $answer->answer
23            ];
24        });
25    }
26}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Resources/Api/V2/ApiResponse.php.html b/tests/coverage/Http/Resources/Api/V2/ApiResponse.php.html new file mode 100644 index 0000000..0c13e85 --- /dev/null +++ b/tests/coverage/Http/Resources/Api/V2/ApiResponse.php.html @@ -0,0 +1,218 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Resources/Api/V2/ApiResponse.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
ApiResponse
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Enums\ApiStatus;
6use Illuminate\Http\Resources\Json\JsonResource;
7use stdClass;
8
9class ApiResponse extends JsonResource
10{
11    /** @var stdClass */
12    protected $data;
13
14    /** @var \App\Enums\ApiStatus */
15    protected $status;
16
17    /**
18     * @param stdClass  $data
19     * @param ApiStatus $status
20     */
21    public function __construct(stdClass $data = null, ApiStatus $status)
22    {
23        $this->status = $status;
24        $this->data   = $data;
25    }
26
27    /**
28     * Transform the resource into an array.
29     *
30     * @param  mixed  $request
31     * @return array
32     */
33    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
34    public function toArray($request): array
35    {
36        return [
37            'status' => $this->status->value,
38            'data' => (array)$this->data,
39            'version' => config('api.version')
40        ];
41    }
42}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Resources/Api/V2/BrandStatsResource.php.html b/tests/coverage/Http/Resources/Api/V2/BrandStatsResource.php.html new file mode 100644 index 0000000..2ce4bb2 --- /dev/null +++ b/tests/coverage/Http/Resources/Api/V2/BrandStatsResource.php.html @@ -0,0 +1,188 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Resources/Api/V2/BrandStatsResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
BrandStatsResource
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Models\BrandStat;
6use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
7
8class BrandStatsResource extends JsonResourceCollection
9{
10    /**
11     * Transform the resource into an array.
12     *
13     * @param  mixed  $request
14     * @return array
15     */
16    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
17    public function toArray($request)
18    {
19        return [
20            'stats' => $this->collection->map(function (BrandStat $brandStat) {
21                return [
22                    'date' => $brandStat->created_at->format('Y-m-d'),
23                    'active' => $brandStat->active,
24                    'inactive' => $brandStat->inactive,
25                    'old_accounts' => $brandStat->old_accounts,
26                    'new_accounts' => $brandStat->new_accounts,
27                    'total_accounts' => $brandStat->total_accounts,
28                    'total_opt_in' => $brandStat->total_opt_in
29                ];
30            })
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Resources/Api/V2/CustomerResource.php.html b/tests/coverage/Http/Resources/Api/V2/CustomerResource.php.html new file mode 100644 index 0000000..4e5af3e --- /dev/null +++ b/tests/coverage/Http/Resources/Api/V2/CustomerResource.php.html @@ -0,0 +1,297 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Resources/Api/V2/CustomerResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
CustomerResource
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
 getSkeleton
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Models\Brand;
6use App\Models\Customer;
7use Illuminate\Http\Resources\Json\JsonResource;
8
9class CustomerResource extends JsonResource
10{
11    /** @var \App\Models\Customer */
12    private $customer;
13
14    /** @var \App\Models\CustomerInfo */
15    private $info;
16
17    /** @var \App\Models\CustomerLogin */
18    private $login;
19
20    /** @var array */
21    private static $skeleton = [
22        'hashcode' => '',
23        'firstname' => '',
24        'lastname' => '',
25        'email' => '',
26        'birth_date' => '',
27        'address' => '',
28        'address2' => '',
29        'phone' => '',
30        'country' => '',
31        'city' => '',
32        'postcode' => '',
33        'opt_in' => '',
34        'status' => '',
35        'last_connection' => ''
36    ];
37
38    /**
39     * CustomerResource
40     *
41     * @param Brand    $brand
42     * @param Customer $customer
43     */
44    public function __construct(Brand $brand, Customer $customer)
45    {
46        $this->customer = $customer;
47        $this->info     = $customer->info($brand, Customer::FIRST_OR_FAIL);
48        $this->login    = $customer->login($brand, Customer::FIRST_OR_FAIL);
49    }
50
51    /**
52     * Transform the resource into an array.
53     *
54     * @param \Illuminate\Http\Request|null $request
55     * @return array
56     * + email string
57     * + firstname string
58     * + lastname string
59     * + birth_date datetime
60     * + address string
61     * + address2 string
62     * + phone string
63     * + country string
64     * + city string
65     * + postcode string
66     * + opt_in boolean
67     * + hashcode string
68     * + status boolean
69     * + last_connection date
70     */
71    //phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
72    public function toArray($request = null)
73    {
74        $data = array_intersect_key(
75            array_merge(
76                [
77                    'email' => $this->customer->email
78                ],
79                $this->login->toArray(),
80                $this->info->toArray(),
81                ['status' => $this->login->status]
82            ),
83            $this->getSkeleton()
84        );
85
86        $data['country']  = $this->info->country->value;
87        $data['address2'] = $this->info->address2 ?? '';
88        return $data;
89    }
90
91    /**
92     * Returns the resource skeletton
93     *
94     * @return array
95     */
96    public static function getSkeleton(): array
97    {
98        return self::$skeleton;
99    }
100}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Resources/Api/V2/QuestionStatsResource.php.html b/tests/coverage/Http/Resources/Api/V2/QuestionStatsResource.php.html new file mode 100644 index 0000000..ae4f13f --- /dev/null +++ b/tests/coverage/Http/Resources/Api/V2/QuestionStatsResource.php.html @@ -0,0 +1,217 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Resources/Api/V2/QuestionStatsResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
QuestionStatsResource
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Models\Question;
6use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
7use Illuminate\Support\Collection;
8
9class QuestionStatsResource extends JsonResourceCollection
10{
11    /** @var \App\Models\Question */
12    protected $question = null;
13
14    /**
15     * Create a new resource instance.
16     *
17     * @param  \Illuminate\Support\Collection $questionStats
18     * @param  \App\Models\Question           $question
19     * @return void
20     */
21    public function __construct(Collection $questionStats = null, Question $question)
22    {
23        $this->question = $question;
24        parent::__construct($questionStats);
25    }
26
27    /**
28     * Transform the resource into an array.
29     *
30     * @param  mixed $request
31     * @return array
32     */
33    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
34    public function toArray($request)
35    {
36        return [
37            'answers' => (new AnswersResource($this->question->answers))->toArray($request),
38            'stats' => $this->collection->toArray()
39        ];
40    }
41}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Resources/Api/V2/QuestionsResource.php.html b/tests/coverage/Http/Resources/Api/V2/QuestionsResource.php.html new file mode 100644 index 0000000..0e89ca2 --- /dev/null +++ b/tests/coverage/Http/Resources/Api/V2/QuestionsResource.php.html @@ -0,0 +1,230 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Resources/Api/V2/QuestionsResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
QuestionsResource
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
3
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Models\Answer;
6use App\Models\Question;
7use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
8use Illuminate\Support\Collection;
9
10class QuestionsResource extends JsonResourceCollection
11{
12    /** @var \Illuminate\Support\Collection */
13    protected $userAnwsers = null;
14
15    /**
16     * Create a new resource instance.
17     *
18     * @param  mixed                          $resource
19     * @param  \Illuminate\Support\Collection $userAnwsers
20     * @return void
21     */
22    public function __construct($resource, Collection $userAnwsers = null)
23    {
24        $this->userAnwsers = $userAnwsers;
25        parent::__construct($resource);
26    }
27
28    /**
29     * Transform the resource into an array.
30     *
31     * @param  mixed  $request
32     * @return array
33     */
34    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
35    public function toArray($request)
36    {
37        return $this->collection->map(function (Question $question) {
38            /** @var \Illuminate\Support\Collection */
39            $answers  = $question->answers;
40            $question = [
41                'id' => $question->id,
42                'type' => $question->type->value,
43                'question' => $question->question,
44                'answers' => new AnswersResource($answers)
45            ];
46            if ($this->userAnwsers) {
47                $question['customerAnswers'] = new AnswersResource($answers->filter(function (Answer $answer) {
48                    return $this->userAnwsers->keyBy('id')->has($answer->id);
49                }));
50            }
51            return $question;
52        });
53    }
54}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Resources/Api/V2/dashboard.html b/tests/coverage/Http/Resources/Api/V2/dashboard.html new file mode 100644 index 0000000..e193e7c --- /dev/null +++ b/tests/coverage/Http/Resources/Api/V2/dashboard.html @@ -0,0 +1,287 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Resources/Api/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Resources/Api/V2/index.html b/tests/coverage/Http/Resources/Api/V2/index.html new file mode 100644 index 0000000..4b0e0fb --- /dev/null +++ b/tests/coverage/Http/Resources/Api/V2/index.html @@ -0,0 +1,262 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Resources/Api/V2 + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
AnswersResource.php
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiResponse.php
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
BrandStatsResource.php
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CustomerResource.php
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
QuestionStatsResource.php
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
QuestionsResource.php
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/Http/Resources/Api/dashboard.html b/tests/coverage/Http/Resources/Api/dashboard.html new file mode 100644 index 0000000..7e245ea --- /dev/null +++ b/tests/coverage/Http/Resources/Api/dashboard.html @@ -0,0 +1,286 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Resources/Api + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Resources/Api/index.html b/tests/coverage/Http/Resources/Api/index.html new file mode 100644 index 0000000..e3106d9 --- /dev/null +++ b/tests/coverage/Http/Resources/Api/index.html @@ -0,0 +1,121 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Resources/Api + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
V2
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ +
+ + diff --git a/tests/coverage/Http/Resources/CustomerResource.php.html b/tests/coverage/Http/Resources/CustomerResource.php.html new file mode 100644 index 0000000..b858f26 --- /dev/null +++ b/tests/coverage/Http/Resources/CustomerResource.php.html @@ -0,0 +1,180 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Resources/CustomerResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerResource
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 toArray
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources;
4
5use Illuminate\Http\Resources\Json\JsonResource;
6
7/**
8 * @codeCoverageIgnore
9 */
10class CustomerResource extends JsonResource
11{
12    /** @var integer */
13    private $brandId;
14
15    /**
16     * @param mixed $resource
17     * @param mixed $brandId
18     */
19    public function __construct($resource, $brandId)
20    {
21        parent::__construct($resource);
22        $this->brandId = $brandId;
23    }
24
25    /**
26     * Transform the resource into an array.
27     *
28     * @param  mixed  $request
29     * @return array
30     */
31    //phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
32    public function toArray($request)
33    {
34        $infos = $this->infos()->where('brand_id', $this->brandId)->first();
35        $login = $this->logins()->where('brand_id', $this->brandId)->first();
36        return [
37            'status' => 'OK',
38            'user' => [
39                'crm_id' => $this->id,
40                'firstname' => $infos->firstname,
41                'lastname' => $infos->lastname,
42                'email' => $this->email,
43                'birth_date' => $infos->birth_date,
44                'address' => $infos->address,
45                'address2' => $infos->address2,
46                'phone' => $infos->phone,
47                'country' => $infos->country->label,
48                'city' => $infos->city,
49                'postcode' => $infos->postcode,
50                'opt_in' => $infos->opt_in,
51                'hashcode' => $infos->hashcode,
52                'status' => $login->status,
53            ]
54        ];
55    }
56}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Resources/StatusResource.php.html b/tests/coverage/Http/Resources/StatusResource.php.html new file mode 100644 index 0000000..05262ea --- /dev/null +++ b/tests/coverage/Http/Resources/StatusResource.php.html @@ -0,0 +1,114 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Resources/StatusResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StatusResource
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources;
4
5use Illuminate\Http\Resources\Json\JsonResource;
6
7/**
8 * @codeCoverageIgnore
9 */
10class StatusResource extends JsonResource
11{
12}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Resources/dashboard.html b/tests/coverage/Http/Resources/dashboard.html new file mode 100644 index 0000000..2e35848 --- /dev/null +++ b/tests/coverage/Http/Resources/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Resources + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Resources/index.html b/tests/coverage/Http/Resources/index.html new file mode 100644 index 0000000..2f42518 --- /dev/null +++ b/tests/coverage/Http/Resources/index.html @@ -0,0 +1,146 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Resources + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
Api
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
CustomerResource.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StatusResource.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Http/Sanitizers/RemoveSpaces.php.html b/tests/coverage/Http/Sanitizers/RemoveSpaces.php.html new file mode 100644 index 0000000..19b7e56 --- /dev/null +++ b/tests/coverage/Http/Sanitizers/RemoveSpaces.php.html @@ -0,0 +1,170 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Sanitizers/RemoveSpaces.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
RemoveSpaces
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 sanitize
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Sanitizers;
4
5use ArondeParon\RequestSanitizer\Contracts\Sanitizer;
6
7class RemoveSpaces implements Sanitizer
8{
9    /**
10     * @param mixed $input
11     * @return string
12     */
13    public function sanitize($input): string
14    {
15        return preg_replace('~\s{1,}~', '', $input);
16    }
17}
+ + + +
+ + + + + + diff --git a/tests/coverage/Http/Sanitizers/dashboard.html b/tests/coverage/Http/Sanitizers/dashboard.html new file mode 100644 index 0000000..ffb538e --- /dev/null +++ b/tests/coverage/Http/Sanitizers/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http/Sanitizers + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/Sanitizers/index.html b/tests/coverage/Http/Sanitizers/index.html new file mode 100644 index 0000000..8986960 --- /dev/null +++ b/tests/coverage/Http/Sanitizers/index.html @@ -0,0 +1,120 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http/Sanitizers + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
RemoveSpaces.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/Http/dashboard.html b/tests/coverage/Http/dashboard.html new file mode 100644 index 0000000..8d3d14d --- /dev/null +++ b/tests/coverage/Http/dashboard.html @@ -0,0 +1,292 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Http + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+ +
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
rules77%
rules89%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + +
MethodCRAP
rules4
rules3
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Http/index.html b/tests/coverage/Http/index.html new file mode 100644 index 0000000..72ff2e9 --- /dev/null +++ b/tests/coverage/Http/index.html @@ -0,0 +1,244 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Http + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 99.11% covered (success) +
+
+
99.11%
443 / 447
+
+ 96.23% covered (success) +
+
+
96.23%
51 / 53
+
+ 93.10% covered (success) +
+
+
93.10%
27 / 29
Controllers
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
Middleware
+
+ 100.00% covered (success) +
+
+
100.00%
37 / 37
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
Requests
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
Resources
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
Sanitizers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Kernel.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Jobs/SetServiceStats.php.html b/tests/coverage/Jobs/SetServiceStats.php.html new file mode 100644 index 0000000..c8c125c --- /dev/null +++ b/tests/coverage/Jobs/SetServiceStats.php.html @@ -0,0 +1,227 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Jobs/SetServiceStats.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
SetServiceStats
n/a
0 / 0
n/a
0 / 0
5
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 handle
n/a
0 / 0
2
n/a
0 / 0
 retryUntil
n/a
0 / 0
1
n/a
0 / 0
 middleware
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Jobs;
4
5use App\Models\Service;
6use App\Models\ServiceStat;
7use Illuminate\Bus\Queueable;
8use Illuminate\Contracts\Queue\ShouldQueue;
9use Illuminate\Foundation\Bus\Dispatchable;
10use Illuminate\Queue\InteractsWithQueue;
11use Illuminate\Queue\Middleware\WithoutOverlapping;
12use Illuminate\Queue\SerializesModels;
13
14/**
15 * @codeCoverageIgnore
16 */
17class SetServiceStats implements ShouldQueue
18{
19    use Dispatchable;
20    use InteractsWithQueue;
21    use Queueable;
22    use SerializesModels;
23
24    private const KEY = '2202020';
25
26    /** @var \App\Models\Service */
27    protected $service;
28
29    /**
30     * Create a new job instance.
31     *
32     * @param Service $service
33     * @return void
34     */
35    public function __construct(Service $service)
36    {
37        $this->service = $service;
38    }
39
40    /**
41     * Execute the job.
42     *
43     * @return void
44     */
45    public function handle()
46    {
47        $serviceStat = ServiceStat::firstOrNew([
48            'date' => date('Y-m-d'),
49            'service_id' => $this->service->id,
50            'brand_id' => $this->service->brand->id,
51        ]);
52        if (!$serviceStat->exists) {
53            $serviceStat->service()->associate($this->service);
54            $serviceStat->brand()->associate($this->service->brand);
55        }
56        $serviceStat->nb_calls += 1;
57        $serviceStat->saveOrFail();
58    }
59
60    /**
61     * Determine the time at which the job should timeout.
62     *
63     * @return \DateTime
64     */
65    public function retryUntil()
66    {
67        return now()->addMinutes(rand(0, 360));
68    }
69
70    /**
71     * Get the middleware the job should pass through.
72     *
73     * @return array
74     */
75    public function middleware()
76    {
77        return [
78            (new WithoutOverlapping(self::KEY))
79                ->releaseAfter(rand(90, 180))
80        ];
81    }
82}
+ + + +
+ + + + + + diff --git a/tests/coverage/Jobs/StartStatJobs.php.html b/tests/coverage/Jobs/StartStatJobs.php.html new file mode 100644 index 0000000..f6ec5d8 --- /dev/null +++ b/tests/coverage/Jobs/StartStatJobs.php.html @@ -0,0 +1,145 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Jobs/StartStatJobs.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StartStatJobs
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 handle
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Jobs;
4
5use App\Models\Brand;
6use Illuminate\Bus\Queueable;
7use Illuminate\Contracts\Queue\ShouldQueue;
8use Illuminate\Foundation\Bus\Dispatchable;
9use Illuminate\Queue\InteractsWithQueue;
10
11/**
12 * @codeCoverageIgnore
13 */
14class StartStatJobs implements ShouldQueue
15{
16    use Dispatchable;
17    use InteractsWithQueue;
18    use Queueable;
19
20    /**
21     * Execute the job.
22     *
23     * @return void
24     */
25    public function handle()
26    {
27        $brands = Brand::all();
28        foreach ($brands as $brand) {
29            \dispatch(new StatJob($brand))
30                ->delay(rand(0, 90));
31        }
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/Jobs/StatJob.php.html b/tests/coverage/Jobs/StatJob.php.html new file mode 100644 index 0000000..0706928 --- /dev/null +++ b/tests/coverage/Jobs/StatJob.php.html @@ -0,0 +1,220 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Jobs/StatJob.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StatJob
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 handle
n/a
0 / 0
1
n/a
0 / 0
 middleware
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Jobs;
4
5use App\Models\Brand;
6use App\Models\BrandStat;
7use App\Models\CustomerInfo;
8use App\Models\CustomerLogin;
9use App\Models\SingleOptin;
10use Illuminate\Bus\Queueable;
11use Illuminate\Contracts\Queue\ShouldQueue;
12use Illuminate\Foundation\Bus\Dispatchable;
13use Illuminate\Queue\InteractsWithQueue;
14use Illuminate\Queue\Middleware\WithoutOverlapping;
15use Illuminate\Queue\SerializesModels;
16use Illuminate\Support\Carbon;
17
18/**
19 * @codeCoverageIgnore
20 */
21class StatJob implements ShouldQueue
22{
23    use Dispatchable;
24    use InteractsWithQueue;
25    use Queueable;
26    use SerializesModels;
27
28    /**
29     * Set high max tries as using prevent overlap
30     *
31     * @var integer
32     */
33    public $tries = 10;
34
35    /** Used to prevent overlaping */
36    private const JOBKEY = 32644296;
37
38    /** @var \App\Models\Brand */
39    private $brand;
40
41    /**
42     * Create a new job instance.
43     *
44     * @param Brand $brand
45     * @return void
46     */
47    public function __construct(Brand $brand)
48    {
49        $this->brand = $brand;
50    }
51
52    /**
53     * Execute the job.
54     *
55     * @return void
56     */
57    public function handle()
58    {
59        $stats = new BrandStat([
60            'active' => CustomerLogin::whereBrandId($this->brand->id)
61                ->whereDate('last_connection', '<', Carbon::now()->subMonths(6))->count(),
62            'inactive' => CustomerLogin::whereBrandId($this->brand->id)
63                ->whereDate('last_connection', '>', Carbon::now()->subMonths(6))->count(),
64            'new_accounts' => CustomerLogin::whereBrandId($this->brand->id)
65                ->whereDate('created_at', '<', Carbon::now()->subMonths(6))->count(),
66            'old_accounts' => CustomerLogin::whereBrandId($this->brand->id)
67                ->whereDate('created_at', '>', Carbon::now()->subMonths(6))->count(),
68            'total_accounts' => CustomerLogin::whereBrandId($this->brand->id)->count(),
69            'total_opt_in' => CustomerInfo::whereBrandId($this->brand->id)
70                ->whereOptIn(true)->count() + SingleOptin::whereBrandId($this->brand->id)->count()
71        ]);
72        $stats->brand()->associate($this->brand);
73        $stats->saveOrFail();
74    }
75
76    /**
77     * Get the middleware the job should pass through.
78     *
79     * @return array
80     */
81    public function middleware()
82    {
83        // * Dispatch randomly overlaping jobs if overlapping.
84        return [(new WithoutOverlapping(self::JOBKEY))->releaseAfter(rand(20, 90))];
85    }
86}
+ + + +
+ + + + + + diff --git a/tests/coverage/Jobs/UpdateCustomerLastConnection.php.html b/tests/coverage/Jobs/UpdateCustomerLastConnection.php.html new file mode 100644 index 0000000..14413ce --- /dev/null +++ b/tests/coverage/Jobs/UpdateCustomerLastConnection.php.html @@ -0,0 +1,186 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Jobs/UpdateCustomerLastConnection.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
UpdateCustomerLastConnection
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 handle
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Jobs;
4
5use App\Models\Customer;
6use App\Models\CustomerLogin;
7use Illuminate\Bus\Queueable;
8use Illuminate\Contracts\Queue\ShouldQueue;
9use Illuminate\Foundation\Bus\Dispatchable;
10use Illuminate\Queue\InteractsWithQueue;
11use Illuminate\Queue\SerializesModels;
12use Illuminate\Support\Carbon;
13
14/**
15 * @codeCoverageIgnore
16 */
17class UpdateCustomerLastConnection implements ShouldQueue
18{
19    use Dispatchable;
20    use InteractsWithQueue;
21    use Queueable;
22    use SerializesModels;
23
24    /** @var \Illuminate\Support\Carbon */
25    protected $date = null;
26
27    /** @var \App\Models\Customer */
28    protected $customer = null;
29
30    /** @var \App\Models\CustomerLogin */
31    protected $customerLogin = null;
32
33    /**
34     * Create a new job instance.
35     *
36     * @param Customer      $customer
37     * @param CustomerLogin $customerLogin
38     * @param Carbon        $date
39     * @return void
40     */
41    public function __construct(
42        Customer $customer,
43        CustomerLogin $customerLogin,
44        Carbon $date
45    ) {
46        $this->date          = $date;
47        $this->customer      = $customer;
48        $this->customerLogin = $customerLogin;
49    }
50
51    /**
52     * Execute the job.
53     *
54     * @return void
55     */
56    public function handle()
57    {
58        if ($this->customerLogin->last_connection < $this->date) {
59            $this->customerLogin->last_connection = $this->date;
60            $this->customer->logins()->save($this->customerLogin);
61        }
62    }
63}
+ + + +
+ + + + + + diff --git a/tests/coverage/Jobs/dashboard.html b/tests/coverage/Jobs/dashboard.html new file mode 100644 index 0000000..14e7827 --- /dev/null +++ b/tests/coverage/Jobs/dashboard.html @@ -0,0 +1,284 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Jobs + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Jobs/index.html b/tests/coverage/Jobs/index.html new file mode 100644 index 0000000..523591e --- /dev/null +++ b/tests/coverage/Jobs/index.html @@ -0,0 +1,128 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Jobs + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
SetServiceStats.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StartStatJobs.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StatJob.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
UpdateCustomerLastConnection.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Lib/Helper.php.html b/tests/coverage/Lib/Helper.php.html new file mode 100644 index 0000000..deb828b --- /dev/null +++ b/tests/coverage/Lib/Helper.php.html @@ -0,0 +1,493 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Lib/Helper.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
CRAP
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
Helper
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
28.61
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
 isRequestForApiV2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 isRequestForApiV1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 getRouteName
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 getBearerTokenFromRequest
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
20
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 7
 getRoutePrefix
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 getRoute
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 getPackageInfo
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 signRequest
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
7.10
+
+ 87.50% covered (warning) +
+
+
87.50%
35 / 40
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Lib;
4
5use App\Exceptions\Api\V2\CrmException;
6use Illuminate\Contracts\Encryption\DecryptException;
7use Illuminate\Http\Request;
8use Illuminate\Support\Facades\Crypt;
9use Illuminate\Support\Facades\Route;
10use Illuminate\Support\Str;
11
12/**
13 * Helper Class
14 */
15class Helper
16{
17    /**
18     * Package infos
19     *
20     * @var array
21     */
22    private static $packageInfos;
23
24    /**
25     * Tells if the request is within apiV2 routes
26     *
27     * @param Request $request
28     * @return boolean
29     */
30    public static function isRequestForApiV2(Request $request)
31    {
32        // * Small hack to ensure to know if route is From APIv2.
33        $routeName = static::getRouteName($request) ??
34            (strpos($request->path(), 'api/v2') === 0 ? 'apiV2.' : null) ?? 'none';
35        return strpos($routeName, 'apiV2.') === 0;
36    }
37
38    /**
39     * Tells if the request is within apiV1 routes
40     *
41     * @param Request $request
42     * @return boolean
43     */
44    public static function isRequestForApiV1(Request $request): bool
45    {
46        // * Small hack to ensure to know if route is From APIv1.
47        $routePrefix = (strpos(static::getRoutePrefix($request), 'api/v1') ? 'v1' : null) ??
48            (strpos($request->path(), 'api/v1') === 0 ? 'v1' : null) ?? 'none';
49        return strpos($routePrefix, 'v1') === 0;
50    }
51
52    /**
53     * Try to get current route name
54     *
55     * @param Request $request
56     * @return string|null
57     */
58    public static function getRouteName(Request $request)
59    {
60        return Route::currentRouteName() ?? ($request->route() ? $request->route()->getName() : null);
61    }
62
63    /**
64     * Get the bearer token from request
65     *
66     * @param Request $request
67     * @return string|null
68     */
69    public static function getBearerTokenFromRequest(Request $request): ?string
70    {
71        $header = $request->header('Authorization', '');
72
73        if (Str::contains($header, 'Bearer ')) {
74            $auths = \explode(',', $header);
75            foreach ($auths as $auth) {
76                if (Str::contains($auth, 'Bearer ')) {
77                    return Str::substr($auth, 7);
78                }
79            }
80        }
81        return null;
82    }
83
84    /**
85     * Try to get current route prefix
86     *
87     * @param Request $request
88     * @return string|null
89     */
90    public static function getRoutePrefix(Request $request)
91    {
92        return (Route::getCurrentRoute() ? Route::getCurrentRoute()->getPrefix() : null) ??
93            ($request->route() ? $request->route()->getPrefix() : null);
94    }
95
96    /**
97     * Try to get route from request
98     *
99     * @param Request $request
100     * @return \Illuminate\Routing\Route|null
101     */
102    public static function getRoute(Request $request)
103    {
104        return Route::getRoutes()->getByName(static::getRouteName($request));
105    }
106
107    /**
108     * Get composer.json infos
109     *
110     * @return array
111     */
112    public static function getPackageInfo(): array
113    {
114        if (!static::$packageInfos) {
115            static::$packageInfos = \json_decode(\file_get_contents(__DIR__ . '/../../composer.json'), true);
116        }
117        return static::$packageInfos;
118    }
119
120    /**
121     * Calculate the request signature
122     *
123     * @param string $secret
124     * @param string $uri
125     * @param string $method
126     * @param array  $parameters
127     * @return string request signature
128     * @throws CrmException If an error occurs.
129     */
130    public static function signRequest(string $secret, string $uri, string $method, array $parameters): string
131    {
132        try {
133            $uri    = collect(\parse_url($uri));
134            $method = \strtolower($method);
135
136            // * Remove signature from parameters to calculate the hashmac.
137            $signature = collect([
138                'uri' => sprintf(
139                    '%s://%s%s',
140                    $uri->get('scheme'),
141                    $uri->get('host'),
142                    $uri->get('path')
143                ),
144                'method' => $method
145            ]);
146
147            $flattenRequestArray = function (array $array) use (&$flattenRequestArray) {
148                $out = [];
149                foreach ($array as $key => $value) {
150                    if (is_array($value)) {
151                        $subArray = $flattenRequestArray($value);
152                        foreach ($subArray as $subKey => $subValue) {
153                            $out["$key.$subKey"] = $subValue;
154                        }
155                        continue;
156                    }
157                    $out[$key] = $value;
158                }
159                return $out;
160            };
161
162            $parameters = $flattenRequestArray($parameters);
163            $parameters = collect($parameters);
164
165            // * Remove signature from parameters.
166            $parameters->pull('signature');
167
168            // * Natural sort all parameters.
169            $parameters = $parameters->sortKeys(\SORT_NATURAL)
170                ->map(function ($value, $key) {
171                    return sprintf('%s=%s', \rawurlencode($key), \rawurlencode($value));
172                });
173
174            $signature->push($parameters->implode('&'));
175
176            $calculatedHash = base64_encode(hash_hmac(
177                'sha256',
178                $signature->implode('\n'),
179                Crypt::decryptString($secret),
180                true
181            ));
182
183            if (!$calculatedHash) {
184                throw new CrmException('Failed to calculate requests hash');
185            }
186
187            return $calculatedHash;
188        } catch (DecryptException $e) {
189            throw new CrmException('Failed to decrypt service credentials', 0, $e);
190        } catch (\Exception $e) {
191            throw new CrmException('An error occured when authenticating service', 0, $e);
192        }//end try
193    }
194}
+ + + +
+ + + + + + diff --git a/tests/coverage/Lib/MigrateOldData.php.html b/tests/coverage/Lib/MigrateOldData.php.html new file mode 100644 index 0000000..117a390 --- /dev/null +++ b/tests/coverage/Lib/MigrateOldData.php.html @@ -0,0 +1,389 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Lib/MigrateOldData.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
MigrateOldData
n/a
0 / 0
n/a
0 / 0
19
n/a
0 / 0
 runMigration
n/a
0 / 0
1
n/a
0 / 0
 instruct
n/a
0 / 0
15
n/a
0 / 0
 initFile
n/a
0 / 0
1
n/a
0 / 0
 write
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Lib;
4
5use App\Exceptions\AbortRowImportException;
6use App\Models\Brand;
7use App\Models\Customer;
8use App\Models\CustomerInfo;
9use App\Models\CustomerLogin;
10use App\Models\SingleOptin;
11use Carbon\Exceptions\InvalidFormatException;
12use Database\migrations\LeGaulois2016Migration;
13use Illuminate\Support\Carbon;
14use Illuminate\Support\Facades\File;
15use Illuminate\Support\Facades\Hash;
16use Illuminate\Support\Facades\Validator;
17use Illuminate\Support\Str;
18
19/**
20 * @codeCoverageIgnore
21 */
22class MigrateOldData extends LeGaulois2016Migration
23{
24    /** @var Brand */
25    private $brand;
26
27    /**
28     * runMigration
29     *
30     * @param Brand  $brand
31     * @param string $table
32     * @return void
33     */
34    public function runMigration(Brand $brand, string $table)
35    {
36        $this->table = $table;
37        $this->brand = $brand;
38        $this->instruct();
39        $this->up();
40    }
41
42    /**
43     * instruct
44     *
45     * @return array
46     */
47    // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
48    public function instruct(): array
49    {
50        $this->initFile();
51        $cleanDegeu = function (string $str) {
52            return html_entity_decode(
53                trim(str_replace('\\', '', ucwords(stripcslashes(strtolower($str))))),
54                ENT_QUOTES | ENT_HTML5,
55                'UTF-8'
56            );
57        };
58        return [
59            'Users' => [
60                'table' => $this->table,
61                'model' => Customer::class,
62                'comment' => sprintf('Migrating %s customers', $this->brand->name),
63                'bind' => [
64                    'email' => [
65                        'apply' => function ($v, $model) use ($cleanDegeu) {
66                            $v                      = \mb_convert_encoding($v, "UTF-8");
67                            $v                      = explode('#|#', $v);
68                            $oldId                  = $v[0];
69                            $first_name             = $cleanDegeu($v[1]);
70                            $last_name              = $cleanDegeu($v[2]);
71                            $birth_date             = $v[3];
72                            $address                = $cleanDegeu($v[4]);
73                            $address2               = $cleanDegeu($v[5]);
74                            $city                   = $cleanDegeu($v[6]);
75                            $postcode               = $v[7];
76                            $user_inscritNewsletter = $v[8];
77                            $email                  = strtolower($v[9]);
78                            $pwd                    = $v[10];
79                            $created_at             = $v[11];
80                            $updated_at             = $v[12];
81                            $last_cnx               = $v[13];
82
83                            // * Just save Single optin.
84                            if (empty($first_name) && empty($last_name)) {
85                                $sglOptin           = SingleOptin::where('email', strtolower($email))
86                                    ->where('brand_id', $this->brand->id)->firstOrNew();
87                                $sglOptin->email    = strtolower($email);
88                                $sglOptin->brand_id = $this->brand->id;
89                                $sglOptin->save();
90                            } else {
91                                // * Validate Data.
92                                $d = [
93                                    'first_name' => $first_name,
94                                    'last_name' => $last_name,
95                                    'address' => $address,
96                                    'address2' => $address2,
97                                    'city' => $city,
98                                    'postcode' => $postcode,
99                                ];
100
101                                $validator = Validator::make($d, [
102                                    'first_name' => 'min:2',
103                                    'last_name' => 'min:2'
104                                ]);
105
106                                if ($validator->fails()) {
107                                    throw new AbortRowImportException();
108                                }
109
110                                $rules = [
111                                    'address' => 'string|min:2',
112                                    'address2' => 'string|min:2',
113                                    'city' => 'string|min:2',
114                                    'postcode' => 'digits_between:4,5'
115                                ];
116
117                                $validate = function (array $data, array $rule) {
118                                    return Validator::make($data, $rule)->fails();
119                                };
120
121                                foreach ($rules as $key => $rule) {
122                                    if ($validate([$key => $d[$key]], [$key => $rule])) {
123                                        ${$key} = null;
124                                    }
125                                }
126
127                                $model             = Customer::where('email', $email)->firstOrNew();
128                                $model->email      = $email;
129                                $model->created_at = $created_at;
130                                $model->updated_at = $updated_at;
131                                $model->save();
132
133                                // * Filtre birthdate puisque c'est du sale dans la vielle crm.
134                                try {
135                                    $expl = explode('-', $birth_date);
136                                    $d    = Carbon::parse($birth_date);
137                                    if (
138                                        $d->toDateString() === sprintf(
139                                            '%s-%s-%s',
140                                            $expl[2] ?? '',
141                                            $expl[1] ?? '',
142                                            $expl[0] ?? ''
143                                        )
144                                    ) {
145                                        $birth_date = $d->toDateString();
146                                    }
147                                } catch (InvalidFormatException $e) {
148                                    $birth_date = '';
149                                }
150
151
152                                if ($birth_date === '0000-00-00') {
153                                    $birth_date = '';
154                                }
155
156                                $city     = !empty($city) ? $city : null;
157                                $postcode = !empty($postcode) ? $postcode : null;
158                                $address  = !empty($address) ? $address : null;
159                                $address2 = !empty($address2) ? $address2 : null;
160
161                                $cInfo              = new CustomerInfo();
162                                $cInfo->hashcode    = Str::uuid();
163                                $cInfo->firstname   = ucfirst(strtolower($first_name));
164                                $cInfo->lastname    = ucfirst(strtolower($last_name));
165                                $cInfo->birth_date  = !strlen($birth_date) ? null : $birth_date;
166                                $cInfo->address     = $address;
167                                $cInfo->address2    = $address2;
168                                $cInfo->city        = $city;
169                                $cInfo->postcode    = $postcode;
170                                $cInfo->opt_in      = $user_inscritNewsletter ? true : false;
171                                $cInfo->brand_id    = $this->brand->id;
172                                $cInfo->customer_id = $model->id;
173                                $cInfo->created_at  = $created_at;
174                                $cInfo->updated_at  = $updated_at;
175                                $cInfo->save();
176
177                                $cLogin                  = new CustomerLogin();
178                                $cLogin->password        = Hash::make(trim($pwd));
179                                $cLogin->brand_id        = $this->brand->id;
180                                $cLogin->customer_id     = $model->id;
181                                $cLogin->status          = true;
182                                $cLogin->created_at      = $created_at;
183                                $cLogin->updated_at      = $updated_at;
184                                $cLogin->last_connection = $last_cnx;
185                                $cLogin->save();
186
187                                $this->write(
188                                    [
189                                        'new' => $model->id,
190                                        'old' => $oldId,
191                                        'hashcode' => $cInfo->hashcode,
192                                        'created_at' => $created_at,
193                                        'updated_at' => $updated_at
194                                    ]
195                                );
196                            }//end if
197                            throw new AbortRowImportException();
198                        },
199                        'name' => [
200                            'user_id',
201                            'firstname',
202                            'lastname',
203                            'birthdate',
204                            'address',
205                            'address2',
206                            'city',
207                            'postcode',
208                            'optin',
209                            'email',
210                            'pwd',
211                            'created_at',
212                            'updated_at',
213                            'last_cnx'
214                        ]
215                    ],
216                ]
217            ],
218        ];
219    }
220
221    /**
222     * Create the migration file
223     *
224     * @return void
225     */
226    public function initFile()
227    {
228        File::put(storage_path('crm_migration.csv'), sprintf('new;old;hashcode;created_at;updated_at') . PHP_EOL);
229    }
230
231    /**
232     * Write date in the migration file
233     *
234     * @param array $data
235     * @return void
236     */
237    public function write(array $data)
238    {
239        foreach ($data as &$d) {
240            $d = sprintf('"%s"', $d);
241        }
242        File::append(storage_path('crm_migration.csv'), implode(';', $data) . PHP_EOL);
243    }
244}
+ + + +
+ + + + + + diff --git a/tests/coverage/Lib/dashboard.html b/tests/coverage/Lib/dashboard.html new file mode 100644 index 0000000..49b6bd1 --- /dev/null +++ b/tests/coverage/Lib/dashboard.html @@ -0,0 +1,290 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Lib + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + +
ClassCoverage
App\Lib\Helper80%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + +
ClassCRAP
App\Lib\Helper28
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
getBearerTokenFromRequest0%
signRequest87%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + +
MethodCRAP
getBearerTokenFromRequest20
signRequest7
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Lib/index.html b/tests/coverage/Lib/index.html new file mode 100644 index 0000000..a7c5471 --- /dev/null +++ b/tests/coverage/Lib/index.html @@ -0,0 +1,132 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Lib + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
Helper.php
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
MigrateOldData.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Mail/CourrierArcadiaError.php.html b/tests/coverage/Mail/CourrierArcadiaError.php.html new file mode 100644 index 0000000..aed22bd --- /dev/null +++ b/tests/coverage/Mail/CourrierArcadiaError.php.html @@ -0,0 +1,187 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Mail/CourrierArcadiaError.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CourrierArcadiaError
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 build
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Mail;
4
5use Illuminate\Bus\Queueable;
6use Illuminate\Contracts\Queue\ShouldQueue;
7use Illuminate\Mail\Mailable;
8use Illuminate\Queue\SerializesModels;
9
10/**
11 * @codeCoverageIgnore
12 */
13class CourrierArcadiaError extends Mailable implements ShouldQueue
14{
15    use Queueable;
16    use SerializesModels;
17
18    /** @var string */
19    public $subject;
20
21    /** @var string */
22    public $msg;
23
24    /** @var string */
25    public $xml;
26
27    /**
28     * Create a new message instance.
29     *
30     * @param string $subject
31     * @param string $msg
32     * @param string $xml
33     * @return void
34     */
35    public function __construct(string $subject, string $msg, string $xml)
36    {
37        $this->subject = $subject;
38        $this->msg     = $msg;
39        $this->xml     = $xml;
40    }
41
42    /**
43     * Build the message.
44     *
45     * @return $this
46     */
47    public function build()
48    {
49        $msg = $this->msg;
50        return $this->subject(
51            sprintf(
52                "[LDC CRM] %s",
53                $this->subject
54            )
55        )->view(
56            'mail.arcadia-error',
57            compact('msg')
58        )->attachData(
59            $this->xml,
60            date("Ymd_His") . '.xml',
61            ['mime' => 'application/xml']
62        );
63    }
64}
+ + + +
+ + + + + + diff --git a/tests/coverage/Mail/ServiceCredentialRenewed.php.html b/tests/coverage/Mail/ServiceCredentialRenewed.php.html new file mode 100644 index 0000000..fbe0be9 --- /dev/null +++ b/tests/coverage/Mail/ServiceCredentialRenewed.php.html @@ -0,0 +1,182 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Mail/ServiceCredentialRenewed.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ServiceCredentialRenewed
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 build
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Mail;
4
5use App\Models\Service;
6use App\Models\User;
7use Illuminate\Mail\Mailable;
8
9/**
10 * @codeCoverageIgnore
11 */
12class ServiceCredentialRenewed extends Mailable
13{
14    /**
15     * The concerned service
16     *
17     * @var \App\Models\Service
18     */
19    public $service;
20
21    /**
22     * The user who did the action
23     *
24     * @var \App\Models\User
25     */
26    public $user;
27
28    /**
29     * Create a new message instance.
30     *
31     * @param Service $service
32     * @param User    $user
33     * @return void
34     */
35    public function __construct(Service $service, User $user)
36    {
37        $this->service = $service;
38        $this->user    = $user;
39    }
40
41    /**
42     * Build the message.
43     *
44     * @return $this
45     */
46    public function build()
47    {
48        return $this->view('mail.service-renewed')
49            ->subject('!!! Warning !!!, New service credential have been generated !')
50            ->text('mail.service-renewed-plain')
51            ->with('appName', config('app.name'))
52            ->with('service', $this->service)
53            ->with('by', sprintf('%s %s (%s)', $this->user->firstname, $this->user->lastname, $this->user->email))
54            ->to(
55                config('mail.contactsList.serviceweb', 'equipe.web@ldc.fr'),
56                sprintf('%s service warning', config('app.name'))
57            );
58    }
59}
+ + + +
+ + + + + + diff --git a/tests/coverage/Mail/dashboard.html b/tests/coverage/Mail/dashboard.html new file mode 100644 index 0000000..e1ed266 --- /dev/null +++ b/tests/coverage/Mail/dashboard.html @@ -0,0 +1,284 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Mail + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Mail/index.html b/tests/coverage/Mail/index.html new file mode 100644 index 0000000..ea567c1 --- /dev/null +++ b/tests/coverage/Mail/index.html @@ -0,0 +1,102 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Mail + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
CourrierArcadiaError.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ServiceCredentialRenewed.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Models/Answer.php.html b/tests/coverage/Models/Answer.php.html new file mode 100644 index 0000000..21bc954 --- /dev/null +++ b/tests/coverage/Models/Answer.php.html @@ -0,0 +1,273 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/Answer.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
Answer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
 question
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 customers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7use Illuminate\Support\Str;
8use Kwaadpepper\CrudPolicies\Enums\CrudAction;
9use Kwaadpepper\CrudPolicies\Enums\CrudType;
10use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
11
12class Answer extends Model
13{
14    use HasFactory;
15    use IsCrudModel;
16
17    /** @var string */
18    protected $crudLabelColumn = 'answer';
19
20    /** @var string */
21    protected $crudValueColumn = 'id';
22
23    /**
24     * The attributes that are mass assignable.
25     *
26     * @var string[]
27     */
28    protected $fillable = [
29        'answer'
30    ];
31
32    /**
33     * Answer
34     *
35     * @param array $attributes
36     */
37    public function __construct(array $attributes = [])
38    {
39        self::$editableProperties = [
40            'answer' => [
41                'label' => Str::ucfirst(trans('models.common.answer')),
42                'type' => CrudType::string(),
43                'validate' => [
44                    'min:1',
45                    'max:255'
46                ],
47                'actions' => [
48                    CrudAction::viewAny(),
49                    CrudAction::view(),
50                    CrudAction::create(),
51                    CrudAction::update()
52                ]
53            ]
54        ];
55        parent::__construct($attributes);
56    }
57
58    // * RELATIONS
59
60    /**
61     * Question BelongsTo relation
62     *
63     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
64     */
65    public function question()
66    {
67        return $this->belongsTo(Question::class);
68    }
69
70    /**
71     * Answer belongsToMany relation
72     *
73     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
74     */
75    public function customers()
76    {
77        return $this->belongsToMany(Customer::class, 'customer_answer');
78    }
79}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/Brand.php.html b/tests/coverage/Models/Brand.php.html new file mode 100644 index 0000000..755452b --- /dev/null +++ b/tests/coverage/Models/Brand.php.html @@ -0,0 +1,519 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/Brand.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 40.00% covered (danger) +
+
+
40.00%
4 / 10
CRAP
+
+ 71.43% covered (warning) +
+
+
71.43%
20 / 28
Brand
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 40.00% covered (danger) +
+
+
40.00%
4 / 10
12.33
+
+ 71.43% covered (warning) +
+
+
71.43%
20 / 28
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
 getCacheBaseTags
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 booted
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
1.03
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
 services
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 questions
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 customerInfos
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 servicesStats
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 singleOptins
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 getAccountsAttribute
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 getTotalOptinsAttribute
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7use Illuminate\Support\Str;
8use Kwaadpepper\CrudPolicies\Enums\CrudAction;
9use Kwaadpepper\CrudPolicies\Enums\CrudType;
10use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
11use Rennokki\QueryCache\Traits\QueryCacheable;
12
13class Brand extends Model
14{
15    use IsCrudModel;
16    use HasFactory;
17    use QueryCacheable;
18
19    /** @var string */
20    protected $crudLabelColumn = 'name';
21
22    /** @var string */
23    protected $crudValueColumn = 'id';
24
25    /**
26     * The timeout cache for this model
27     *
28     * @var integer
29     */
30    protected $cacheFor = 0;
31
32    /**
33     * The attributes that are mass assignable.
34     *
35     * @var string[]
36     */
37    protected $fillable = [
38        'name'
39    ];
40
41    /**
42     * Brand
43     *
44     * @param array $attributes
45     */
46    public function __construct(array $attributes = [])
47    {
48        $this->cacheFor = config('api.servicesDBCache') * 60;
49
50        self::$editableProperties = [
51            'name' => [
52                'label' => Str::ucfirst(trans('models.common.name')),
53                'type' => CrudType::string(),
54                'validate' => [
55                    'min:5',
56                    'max:255'
57                ],
58                'actions' => [
59                    CrudAction::viewAny(),
60                    CrudAction::view(),
61                    CrudAction::create(),
62                    CrudAction::update()
63                ]
64            ]
65        ];
66        parent::__construct($attributes);
67    }
68
69    /**
70     * Set the base cache tags that will be present
71     * on all queries.
72     *
73     * ! This is used to flush caching
74     *
75     * @return array
76     */
77    protected function getCacheBaseTags(): array
78    {
79        return [
80            'crm_brands',
81        ];
82    }
83
84    /**
85     * Perform any actions required after the model boots.
86     *
87     * @return void
88     */
89    protected static function booted()
90    {
91        static::created(function () {
92            // * Flush Eloquent cache.
93            self::flushQueryCache();
94        });
95        static::updated(function () {
96            // * Flush Eloquent cache.
97            self::flushQueryCache();
98        });
99        static::deleted(function () {
100            // * Flush Eloquent cache.
101            self::flushQueryCache();
102        });
103    }
104
105    // * RELATIONS
106
107    /**
108     * Service hasMany relation
109     *
110     * @return \Illuminate\Database\Eloquent\Relations\HasMany
111     */
112    public function services()
113    {
114        return $this->hasMany(Service::class);
115    }
116
117    /**
118     * Question hasMany relation
119     *
120     * @return \Illuminate\Database\Eloquent\Relations\HasMany
121     */
122    public function questions()
123    {
124        return $this->hasMany(Question::class);
125    }
126
127    /**
128     * CustomerInfo hasMany relation
129     *
130     * @return \Illuminate\Database\Eloquent\Relations\HasMany
131     */
132    public function customerInfos()
133    {
134        return $this->hasMany(CustomerInfo::class);
135    }
136
137    /**
138     * ServiceStat hasMany relation
139     *
140     * @return \Illuminate\Database\Eloquent\Relations\HasMany
141     */
142    public function servicesStats()
143    {
144        return $this->hasMany(ServiceStat::class);
145    }
146
147    /**
148     * SingleOptin hasMany relation
149     *
150     * @return \Illuminate\Database\Eloquent\Relations\HasMany
151     */
152    public function singleOptins()
153    {
154        return $this->hasMany(SingleOptin::class);
155    }
156
157    // * METHODS
158
159    /**
160     * Account attribute accessor
161     *
162     * @return integer
163     */
164    public function getAccountsAttribute(): int
165    {
166        return $this->customerInfos()->count();
167    }
168
169    /**
170     * Total optins attribute accessor
171     *
172     * @return integer
173     */
174    public function getTotalOptinsAttribute(): int
175    {
176        return $this->customerInfos()->where('opt_in', 1)->count() + $this->singleOptins()->count();
177    }
178}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/BrandStat.php.html b/tests/coverage/Models/BrandStat.php.html new file mode 100644 index 0000000..0a47619 --- /dev/null +++ b/tests/coverage/Models/BrandStat.php.html @@ -0,0 +1,235 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/BrandStat.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
CRAP
+
+ 50.00% covered (danger) +
+
+
50.00%
3 / 6
BrandStat
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
4.12
+
+ 50.00% covered (danger) +
+
+
50.00%
3 / 6
 booted
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.26
+
+ 60.00% covered (warning) +
+
+
60.00%
3 / 5
 brand
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Model;
6use Illuminate\Support\Carbon;
7
8class BrandStat extends Model
9{
10    /**
11     * Indicates if the model should be timestamped.
12     *
13     * @var boolean
14     */
15    public $timestamps = false;
16
17    /**
18     * The attributes that should be cast to native types.
19     *
20     * @var array
21     */
22    public $casts = [
23        'created_at' => 'date'
24    ];
25
26    /**
27     * The attributes that are mass assignable.
28     *
29     * @var string[]
30     */
31    protected $fillable = [
32        'active', 'inactive',
33        'old_accounts', 'new_accounts',
34        'total_accounts', 'total_opt_in'
35    ];
36
37    /**
38     * Perform any actions required after the model boots.
39     *
40     * @return void
41     */
42    protected static function booted()
43    {
44        static::creating(function (BrandStat $stat) {
45            if (!$stat->created_at) {
46                $stat->created_at = Carbon::now();
47            }
48        });
49    }
50
51    // * RELATIONS
52
53    /**
54     * Brand hasMany relation
55     *
56     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
57     */
58    public function brand()
59    {
60        return $this->belongsTo(Brand::class);
61    }
62}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/Customer.php.html b/tests/coverage/Models/Customer.php.html new file mode 100644 index 0000000..7afb92d --- /dev/null +++ b/tests/coverage/Models/Customer.php.html @@ -0,0 +1,553 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/Customer.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
CRAP
+
+ 93.22% covered (success) +
+
+
93.22%
55 / 59
Customer
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
24.18
+
+ 93.22% covered (success) +
+
+
93.22%
55 / 59
 infos
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 logins
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 optInHistory
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 brands
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 answers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 info
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6.01
+
+ 92.86% covered (success) +
+
+
92.86%
13 / 14
 login
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6.01
+
+ 92.86% covered (success) +
+
+
92.86%
13 / 14
 loadBrandData
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
5.12
+
+ 83.33% covered (warning) +
+
+
83.33%
10 / 12
 setOptIn
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 getAnswers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Builder;
6use Illuminate\Database\Eloquent\Collection;
7use Illuminate\Database\Eloquent\Factories\HasFactory;
8use Illuminate\Database\Eloquent\Model;
9use Illuminate\Database\Eloquent\ModelNotFoundException;
10
11class Customer extends Model
12{
13    use HasFactory;
14
15    public const FIRST_OR_NULL = 0;
16    public const FIRST_OR_NEW  = 1;
17    public const FIRST_OR_FAIL = 2;
18
19    /**
20     * The attributes that are mass assignable.
21     *
22     * @var string[]
23     */
24    protected $fillable = [
25        'email'
26    ];
27
28    // * RELATIONS
29
30    /**
31     * CustomerInfo hasMany relation
32     *
33     * @return \Illuminate\Database\Eloquent\Relations\HasMany
34     */
35    public function infos()
36    {
37        return $this->hasMany(CustomerInfo::class);
38    }
39
40    /**
41     * CustomerLogin hasMany relation
42     *
43     * @return \Illuminate\Database\Eloquent\Relations\HasMany
44     */
45    public function logins()
46    {
47        return $this->hasMany(CustomerLogin::class);
48    }
49
50    /**
51     * CustomerOptInHistory hasMany relation
52     *
53     * @return \Illuminate\Database\Eloquent\Relations\HasMany
54     */
55    public function optInHistory()
56    {
57        return $this->hasMany(CustomerOptInHistory::class);
58    }
59
60    /**
61     * Customer Brand belongsToMany relation
62     *
63     * @return \Illuminate\Database\Eloquent\Relations\belongsToMany
64     */
65    public function brands()
66    {
67        return $this->belongsToMany(Brand::class, CustomerInfo::class);
68    }
69
70    /**
71     * Answers belongsToMany relation
72     *
73     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
74     */
75    public function answers()
76    {
77        return $this->belongsToMany(Answer::class, 'customer_answer');
78    }
79
80    // * METHODS
81
82    /**
83     * Get the CustomerInfo from a specific brand
84     * or create a New one if $firstOrNewOrFail is 1
85     * throw model not found exception if firstOrNewOrFail = 2
86     *
87     * @param Brand   $brand
88     * @param integer $firstOrNewOrFail
89     * @return \Illuminate\Database\Eloquent\Builder
90     */
91    public function info(Brand $brand, int $firstOrNewOrFail = self::FIRST_OR_NULL): ?CustomerInfo
92    {
93        if (isset($this->relations['infos'])) {
94            /** @var \App\Models\CustomerInfo $info */
95            foreach ($this->relations['infos'] as $info) {
96                if ($info->brand_id === $brand->id) {
97                    return $info;
98                }
99            }
100        }
101        $query = CustomerInfo::whereHas('brand', function (Builder $query) use ($brand) {
102            $query->where('id', $brand->id);
103        })->whereHas('customer', function (Builder $query) {
104            $query->where('id', $this->id);
105        });
106        if ($firstOrNewOrFail === self::FIRST_OR_NEW) {
107            return $query->firstOrNew();
108        }
109        if ($firstOrNewOrFail === self::FIRST_OR_FAIL) {
110            return $query->firstOrFail();
111        }
112        return $query->first();
113    }
114
115    /**
116     * Get the CustomerLogin from a specific brand
117     * or create a New one if $firstOrNewOrFail is 1
118     * throw model not found exception if firstOrNewOrFail = 2
119     *
120     * @param Brand   $brand
121     * @param integer $firstOrNewOrFail
122     * @return \Illuminate\Database\Eloquent\Builder
123     */
124    public function login(Brand $brand, int $firstOrNewOrFail = self::FIRST_OR_NULL): ?CustomerLogin
125    {
126        if (isset($this->relations['logins'])) {
127            /** @var \App\Models\CustomerLogin $login */
128            foreach ($this->relations['logins'] as $login) {
129                if ($login->brand_id === $brand->id) {
130                    return $login;
131                }
132            }
133        }
134        $query = CustomerLogin::whereHas('brand', function (Builder $query) use ($brand) {
135            $query->where('id', $brand->id);
136        })->whereHas('customer', function (Builder $query) use ($brand) {
137            $query->where('id', $this->id);
138        });
139        if ($firstOrNewOrFail === self::FIRST_OR_NEW) {
140            return $query->firstOrNew();
141        }
142        if ($firstOrNewOrFail === self::FIRST_OR_FAIL) {
143            return $query->firstOrFail();
144        }
145        return $query->first();
146    }
147
148    /**
149     * Load login and info for this customer and this specific brand.
150     *
151     * @param Brand   $brand
152     * @param boolean $orFail
153     * @return self
154     * @throws ModelNotFoundException If no login or info was found for the brand.
155     */
156    public function loadBrandData(Brand $brand, bool $orFail = false): self
157    {
158        $this->load([
159            'logins' => function ($q) use ($brand) {
160                $q->where('brand_id', $brand->id);
161            },
162            'infos' => function ($q) use ($brand) {
163                $q->where('brand_id', $brand->id);
164            }
165        ]);
166        if ($orFail and !isset($this->relations['logins'][0])) {
167            throw (new ModelNotFoundException())->setModel(CustomerLogin::class);
168        }
169        if ($orFail and !isset($this->relations['infos'][0])) {
170            throw (new ModelNotFoundException())->setModel(CustomerInfo::class);
171        }
172        return $this;
173    }
174
175    /**
176     * Set this customer as optin
177     *
178     * @param Brand   $brand
179     * @param boolean $optIn
180     * @return void
181     * @throws \Throwable If customer history update failed.
182     */
183    public function setOptIn(Brand $brand, bool $optIn): void
184    {
185        $this->opt_in = $optIn;
186
187        // * Ajout dans la table d'historique des opt-in de la nouvelle valeur.
188        $optInHistory            = new CustomerOptInHistory();
189        $optInHistory->new_value = $optIn;
190        $optInHistory->customer()->associate($this);
191        $optInHistory->brand()->associate($brand);
192        $optInHistory->saveOrFail();
193    }
194
195    /**
196     * Get All answers given by this customer
197     * for a specific service
198     *
199     * @param Service $service
200     * @return Collection
201     */
202    public function getAnswers(Service $service): Collection
203    {
204        return Answer::whereHas('customers', function ($q) {
205            $q->whereId($this->id);
206        })->whereHas('question', function ($q) use ($service) {
207            $q->whereHas('brand', function ($q) use ($service) {
208                $q->whereId($service->brand->id);
209            });
210        })->get();
211    }
212}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/CustomerAnswer.php.html b/tests/coverage/Models/CustomerAnswer.php.html new file mode 100644 index 0000000..c84b700 --- /dev/null +++ b/tests/coverage/Models/CustomerAnswer.php.html @@ -0,0 +1,235 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/CustomerAnswer.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CustomerAnswer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 getUpdatedAtColumn
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 customer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 answer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Relations\Pivot;
6
7class CustomerAnswer extends Pivot
8{
9    /**
10     * Inhibit update_at column as we only have
11     * created_at
12     *
13     * @return null
14     */
15    public function getUpdatedAtColumn()
16    {
17        return null;
18    }
19
20    // * RELATIONS
21
22    /**
23     * Customer belongsTo relation
24     *
25     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
26     */
27    public function customer()
28    {
29        return $this->belongsTo(Customer::class);
30    }
31
32    /**
33     * Customer belongsTo relation
34     *
35     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
36     */
37    public function answer()
38    {
39        return $this->belongsTo(Answer::class);
40    }
41}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/CustomerInfo.php.html b/tests/coverage/Models/CustomerInfo.php.html new file mode 100644 index 0000000..bdc13fe --- /dev/null +++ b/tests/coverage/Models/CustomerInfo.php.html @@ -0,0 +1,356 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/CustomerInfo.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 80.00% covered (warning) +
+
+
80.00%
4 / 5
CRAP
+
+ 94.74% covered (success) +
+
+
94.74%
18 / 19
CustomerInfo
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 80.00% covered (warning) +
+
+
80.00%
4 / 5
5.00
+
+ 94.74% covered (success) +
+
+
94.74%
18 / 19
 booted
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
 customer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 login
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 getFromEmail
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use App\Enums\Country;
6use Illuminate\Support\Str;
7use Illuminate\Database\Eloquent\Factories\HasFactory;
8use Illuminate\Database\Eloquent\Model;
9use Kwaadpepper\Enum\Traits\CastsEnums;
10
11class CustomerInfo extends Model
12{
13    use HasFactory;
14    use CastsEnums;
15
16    /**
17     * Map attribute names to enum classes.
18     *
19     * @var array
20     */
21    protected $enumCasts = [
22        'country' => Country::class
23    ];
24
25    /**
26     * The attributes that should be cast to native types.
27     *
28     * @var array
29     */
30    protected $casts = [
31        'opt_in' => 'bool'
32    ];
33
34    /**
35     * The attributes that are mass assignable.
36     *
37     * @var string[]
38     */
39    protected $fillable = [
40        'firstname',
41        'lastname',
42        'birth_date',
43        'address',
44        'address2',
45        'phone',
46        'country',
47        'city',
48        'postcode',
49        'opt_in'
50    ];
51
52    /**
53     * Perform any actions required after the model boots.
54     *
55     * @return void
56     */
57    protected static function booted()
58    {
59        static::creating(function (CustomerInfo $customerInfo) {
60            $customerInfo->hashcode = Str::uuid();
61        });
62        static::deleting(function (CustomerInfo $customerInfo) {
63            CustomerOptInHistory::whereHas('customer', function ($q) use ($customerInfo) {
64                $q->whereId($customerInfo->customer->id);
65            })->whereHas('brand', function ($q) use ($customerInfo) {
66                $q->whereId($customerInfo->brand->id);
67            })->delete();
68        });
69    }
70
71    // * RELATIONS
72
73    /**
74     * Customer BelongsTo relation
75     *
76     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
77     */
78    public function customer()
79    {
80        return $this->belongsTo(Customer::class);
81    }
82
83    /**
84     * get Login
85     *
86     * @return \App\Models\CustomerLogin
87     */
88    public function login()
89    {
90        return $this->customer->login($this->brand);
91    }
92
93    /**
94     * Brand BelongsTo relation
95     *
96     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
97     */
98    public function brand()
99    {
100        return $this->belongsTo(Brand::class);
101    }
102
103    // * METHODS
104
105    /**
106     * Get customer info from email
107     *
108     * @param Brand  $brand
109     * @param string $email
110     * @return \Illuminate\Database\Eloquent\Builder
111     */
112    public static function getFromEmail(Brand $brand, string $email)
113    {
114        return CustomerInfo::whereHas('brand', function ($q) use ($brand) {
115            $q->where('id', $brand->id);
116        })->whereHas('customer', function ($q) use ($email) {
117            $q->where('email', $email);
118        });
119    }
120}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/CustomerLogin.php.html b/tests/coverage/Models/CustomerLogin.php.html new file mode 100644 index 0000000..3dccf9a --- /dev/null +++ b/tests/coverage/Models/CustomerLogin.php.html @@ -0,0 +1,307 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/CustomerLogin.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
CustomerLogin
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
6
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
 booted
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
 customer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 passwordMatch
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7use Illuminate\Support\Facades\Hash;
8
9class CustomerLogin extends Model
10{
11    use HasFactory;
12
13    /**
14     * The model's attributes.
15     *
16     * @var array
17     */
18    protected $attributes = [
19        'status' => false
20    ];
21
22    /**
23     * The attributes that are mass assignable.
24     *
25     * @var string[]
26     */
27    protected $fillable = [
28        'password'
29    ];
30
31    /**
32     * The attributes that should be cast to native types.
33     *
34     * @var array
35     */
36    protected $casts = [
37        'status' => 'bool'
38    ];
39
40    /**
41     * Perform any actions required after the model boots.
42     *
43     * @return void
44     */
45    protected static function booted()
46    {
47        static::creating(function ($customerLogin) {
48            if (Hash::needsRehash($customerLogin->password)) {
49                $customerLogin->password = Hash::make(trim($customerLogin->password));
50            }
51        });
52        static::updating(function ($customerLogin) {
53            if (Hash::needsRehash($customerLogin->password)) {
54                $customerLogin->password = Hash::make(trim($customerLogin->password));
55            }
56        });
57    }
58
59    // * RELATIONS
60
61    /**
62     * Customer BelongsTo relation
63     *
64     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
65     */
66    public function customer()
67    {
68        return $this->belongsTo(Customer::class);
69    }
70
71    /**
72     * Brand BelongsTo relation
73     *
74     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
75     */
76    public function brand()
77    {
78        return $this->belongsTo(Brand::class);
79    }
80
81    // * METHODS
82    /**
83     * Check if the user password is correct
84     *
85     * @param string $password
86     * @return boolean
87     */
88    public function passwordMatch(string $password): bool
89    {
90        return Hash::check($password, $this->password);
91    }
92}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/CustomerOptInHistory.php.html b/tests/coverage/Models/CustomerOptInHistory.php.html new file mode 100644 index 0000000..5fd5abe --- /dev/null +++ b/tests/coverage/Models/CustomerOptInHistory.php.html @@ -0,0 +1,213 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/CustomerOptInHistory.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CustomerOptInHistory
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 customer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7
8class CustomerOptInHistory extends Model
9{
10    use HasFactory;
11
12    /**
13     * The table associated with the model.
14     *
15     * @var string
16     */
17    protected $table = "customer_opt_in_history";
18
19    // * RELATIONS
20
21    /**
22     * Customer BelongsTo relation
23     *
24     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
25     */
26    public function customer()
27    {
28        return $this->belongsTo(Customer::class);
29    }
30
31    /**
32     * Brand BelongsTo relation
33     *
34     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
35     */
36    public function brand()
37    {
38        return $this->belongsTo(Brand::class);
39    }
40}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/Question.php.html b/tests/coverage/Models/Question.php.html new file mode 100644 index 0000000..3328793 --- /dev/null +++ b/tests/coverage/Models/Question.php.html @@ -0,0 +1,316 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/Question.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
26 / 26
Question
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
26 / 26
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
24 / 24
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 answers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use App\Enums\QuestionType;
6use Illuminate\Database\Eloquent\Factories\HasFactory;
7use Illuminate\Database\Eloquent\Model;
8use Illuminate\Support\Str;
9use Kwaadpepper\CrudPolicies\Enums\CrudAction;
10use Kwaadpepper\CrudPolicies\Enums\CrudType;
11use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
12use Kwaadpepper\Enum\Traits\CastsEnums;
13
14class Question extends Model
15{
16    use CastsEnums;
17    use HasFactory;
18    use IsCrudModel;
19
20    /** @var string */
21    protected $crudLabelColumn = 'question';
22
23    /** @var string */
24    protected $crudValueColumn = 'id';
25
26    /**
27     * Map attribute names to enum classes.
28     *
29     * @var array
30     */
31    protected $enumCasts = [
32        'type' => QuestionType::class
33    ];
34
35    /**
36     * The attributes that are mass assignable.
37     *
38     * @var string[]
39     */
40    protected $fillable = [
41        'question',
42        'type'
43    ];
44
45    /**
46     * Question
47     *
48     * @param array $attributes
49     */
50    public function __construct(array $attributes = [])
51    {
52        self::$editableProperties = [
53            'question' => [
54                'label' => Str::ucfirst(trans('models.common.question')),
55                'type' => CrudType::string(),
56                'validate' => [
57                    'min:10',
58                    'max:255'
59                ],
60                'actions' => [
61                    CrudAction::viewAny(),
62                    CrudAction::view(),
63                    CrudAction::create(),
64                    CrudAction::update()
65                ]
66            ],
67            'type' => [
68                'label' => Str::ucfirst(trans('models.common.type')),
69                'type' => CrudType::enum(),
70                'enum' => QuestionType::class,
71                'actions' => [
72                    CrudAction::view(),
73                    CrudAction::create(),
74                    CrudAction::update(),
75                ]
76            ],
77            'brand_id' => [
78                'label' => Str::ucfirst(trans('models.classes.brand')),
79                'type' => CrudType::belongsTo(),
80                'belongsTo' => Brand::class,
81                'actions' => [
82                    CrudAction::viewAny(),
83                    CrudAction::view(),
84                    CrudAction::create(),
85                    CrudAction::update(),
86                ]
87            ],
88            'answers' => [
89                'label' => Str::ucfirst(Str::plural(trans('models.classes.answers'))),
90                'type' => CrudType::belongsToMany(),
91                'belongsToMany' => Answer::class,
92                'belongsToManyLabel' => 'answer',
93                'actions' => [
94                    CrudAction::view()
95                ]
96            ]
97        ];
98        parent::__construct($attributes);
99    }
100
101    // * RELATIONS
102
103    /**
104     * Brand BelongsTo relation
105     *
106     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
107     */
108    public function brand()
109    {
110        return $this->belongsTo(Brand::class);
111    }
112
113    /**
114     * Answer BelongsTo relation
115     *
116     * @return \Illuminate\Database\Eloquent\Relations\HasMany
117     */
118    public function answers()
119    {
120        return $this->hasMany(Answer::class);
121    }
122}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/Service.php.html b/tests/coverage/Models/Service.php.html new file mode 100644 index 0000000..2dddab7 --- /dev/null +++ b/tests/coverage/Models/Service.php.html @@ -0,0 +1,607 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/Service.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 60.00% covered (warning) +
+
+
60.00%
6 / 10
CRAP
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
Service
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 60.00% covered (warning) +
+
+
60.00%
6 / 10
15.57
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
16 / 16
 getCacheBaseTags
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 booted
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
1.05
+
+ 64.29% covered (warning) +
+
+
64.29%
9 / 14
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 servicesStats
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 generateBearerToken
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
 generateBearerPassword
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
 incrementStat
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 getFromBearerToken
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
 authenticateApi
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use App\Jobs\SetServiceStats;
6use App\Lib\Helper;
7use Illuminate\Database\Eloquent\Factories\HasFactory;
8use Illuminate\Database\Eloquent\Model;
9use Illuminate\Database\Eloquent\ModelNotFoundException;
10use Illuminate\Http\Request;
11use Illuminate\Support\Facades\Crypt;
12use Illuminate\Support\Facades\Hash;
13use Illuminate\Support\Str;
14use Kwaadpepper\CrudPolicies\Enums\CrudAction;
15use Kwaadpepper\CrudPolicies\Enums\CrudType;
16use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
17use Rennokki\QueryCache\Traits\QueryCacheable;
18use RuntimeException;
19
20class Service extends Model
21{
22    use IsCrudModel;
23    use HasFactory;
24    use QueryCacheable;
25
26    /** @var string */
27    protected $crudLabelColumn = 'name';
28
29    /** @var string */
30    protected $crudValueColumn = 'id';
31
32    /**
33     * The timeout cache for this model
34     *
35     * @var integer
36     */
37    protected $cacheFor = 0;
38
39    /**
40     * The attributes that are mass assignable.
41     *
42     * @var string[]
43     */
44    protected $fillable = [
45        'name', 'brand_id'
46    ];
47
48    /**
49     * Service
50     *
51     * @param array $attributes
52     */
53    public function __construct(array $attributes = [])
54    {
55        $this->cacheFor = config('api.servicesDBCache') * 60;
56
57        self::$editableProperties = [
58            'name' => [
59                'label' => Str::ucfirst(trans('models.common.name')),
60                'type' => CrudType::string(),
61                'validate' => [
62                    'min:5',
63                    'max:255'
64                ],
65                'actions' => [
66                    CrudAction::viewAny(),
67                    CrudAction::view(),
68                    CrudAction::create(),
69                    CrudAction::update()
70                ]
71            ],
72            'brand_id' => [
73                'label' => Str::ucfirst(trans('models.classes.brand')),
74                'type' => CrudType::belongsTo(),
75                'belongsTo' => Brand::class,
76                'actions' => [
77                    CrudAction::viewAny(),
78                    CrudAction::view(),
79                    CrudAction::create(),
80                    CrudAction::update()
81                ]
82            ],
83        ];
84        parent::__construct($attributes);
85    }
86
87    /**
88     * Set the base cache tags that will be present
89     * on all queries.
90     *
91     * ! This is used to flush caching
92     *
93     * @return array
94     */
95    protected function getCacheBaseTags(): array
96    {
97        return [
98            'crm_services',
99        ];
100    }
101
102    /**
103     * Perform any actions required after the model boots.
104     *
105     * @return void
106     */
107    protected static function booted()
108    {
109        static::creating(function (self $service) {
110            $service->generateBearerToken();
111            $service->generateBearerPassword();
112        });
113        static::created(function () {
114            // * Flush Eloquent cache.
115            self::flushQueryCache();
116        });
117        static::updated(function () {
118            // * Flush Eloquent cache.
119            self::flushQueryCache();
120        });
121        static::deleted(function () {
122            // * Flush Eloquent cache.
123            self::flushQueryCache();
124        });
125    }
126
127    // * RELATIONS
128
129    /**
130     * Brand BelongsTo relation
131     *
132     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
133     */
134    public function brand()
135    {
136        return $this->belongsTo(Brand::class);
137    }
138
139    /**
140     * ServiceStat hasMany relation
141     *
142     * @return \Illuminate\Database\Eloquent\Relations\HasMany
143     */
144    public function servicesStats()
145    {
146        return $this->hasMany(ServiceStat::class);
147    }
148
149    // * METHODS
150
151    /**
152     * Generates new bearer token
153     *
154     * @return string
155     */
156    public function generateBearerToken(): string
157    {
158        $this->bearer_token      = hash('ripemd160', Str::uuid());
159        $this->bearer_token_date = now();
160        return $this->bearer_token;
161    }
162
163    /**
164     * Generates new bearer password
165     * It store the crypted password in model
166     * and returns the plain password
167     *
168     * @return string
169     */
170    public function generateBearerPassword(): string
171    {
172        $pwd              = hash('sha256', Str::uuid());
173        $this->bearer_pwd = Crypt::encryptString($pwd);
174        return $pwd;
175    }
176
177    /**
178     * Increment API CRM Stat
179     *
180     * @return void
181     */
182    public function incrementStat(): void
183    {
184        SetServiceStats::dispatch($this)->afterResponse()
185            ->onConnection('database')->onQueue('stats');
186    }
187
188    /**
189     * Get Service from bearerToken with its brand
190     *
191     * TODO remove this method when dropping V1
192     *
193     * @param string $bearerToken
194     * @return \App\Models\Service
195     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException If the token matches no known service.
196     */
197    public static function getFromBearerToken(string $bearerToken): self
198    {
199        $services = static::all();
200        /** @var \App\Models\Service $service */
201        foreach ($services as $service) {
202            // * Keep compatibility with V3.
203            $tokenEquals = false;
204            try {
205                $tokenEquals = Hash::check($bearerToken, $service->bearer_token);
206            } catch (RuntimeException $e) {
207                $tokenEquals = $bearerToken === $service->bearer_token;
208            }
209            if ($service->bearer_token and $tokenEquals) {
210                return $service->load([
211                    'brand' => function ($query) use ($service) {
212                        return $query->cacheTags(["service:{$service->id}:brand"]);
213                    },
214                ]);
215            }
216        }
217        throw new ModelNotFoundException('Could not find service from Service Token');
218    }
219
220    /**
221     * Authenticate the request for this service
222     *
223     * This will check hash-mac and that the request is wellformed
224     *
225     * The request should have the minimum following parameters
226     *  - timestamp using format Y-m-d H:i:s, and must be UTC (Greenwich Mean Time)
227     *  - bearerToken the public token generated by the crm
228     *  - signature the hashmac signature of the request
229     *
230     * ! Signature must be calculated with every parameter urlencode once only
231     * ! All parameters to calculate the signature must be sorted (natural sort asc)
232     *
233     * ! Signature should look like
234     * * httpVerb\n
235     * * uri\n
236     * * queryString
237     *
238     * ! httpVerb as well as uri must be lower case
239     * ! uri should include scheme, host and path and nothing else, if path is empty a trailing slash should be added
240     * ! eg: https://ldc-crm.fr/customer/1  or  https://ldc-crm.fr/
241     * ! The query string shall be separated with '&', eg param1=value1&param2=value2
242     * ! Key names and values of the query string must be url encoded using RFC 3986
243     * ! http://www.faqs.org/rfcs/rfc3986.html
244     * ! Signature must be hmac sha256 using the private key generatedby the crm
245     *
246     *     * ! They should then be concat in a string separated by \n
247     *
248     *
249     *
250     * @param \Illuminate\Http\Request $request
251     * @return boolean
252     * @throws \App\Exceptions\Api\V2\CrmException If an error occurs.
253     */
254    public function authenticateApi(Request $request): bool
255    {
256        $calculatedHash = Helper::signRequest(
257            $this->bearer_pwd,
258            $request->getUri(),
259            $request->getMethod(),
260            $request->all()
261        );
262
263        // ! Très important hash_equals https://fr.wikipedia.org/wiki/Attaque_temporelle
264        return \hash_equals($calculatedHash, $request->signature);
265    }
266}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/ServiceStat.php.html b/tests/coverage/Models/ServiceStat.php.html new file mode 100644 index 0000000..eed0483 --- /dev/null +++ b/tests/coverage/Models/ServiceStat.php.html @@ -0,0 +1,222 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/ServiceStat.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
ServiceStat
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 service
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7
8class ServiceStat extends Model
9{
10    use HasFactory;
11
12    /**
13     * The attributes that are mass assignable.
14     *
15     * @var string[]
16     */
17    protected $fillable = ['date'];
18
19    /**
20     * The model's attributes.
21     *
22     * @var array
23     */
24    protected $attributes = [
25        'nb_calls' => 0
26    ];
27
28    // * RELATIONS
29
30    /**
31     * Brand BelongsTo relation
32     *
33     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
34     */
35    public function brand()
36    {
37        return $this->belongsTo(Brand::class);
38    }
39
40    /**
41     * Service BelongsTo relation
42     *
43     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
44     */
45    public function service()
46    {
47        return $this->belongsTo(Service::class);
48    }
49}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/SingleOptin.php.html b/tests/coverage/Models/SingleOptin.php.html new file mode 100644 index 0000000..87422e1 --- /dev/null +++ b/tests/coverage/Models/SingleOptin.php.html @@ -0,0 +1,273 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/SingleOptin.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 50.00% covered (danger) +
+
+
50.00%
1 / 2
CRAP
+
+ 79.31% covered (warning) +
+
+
79.31%
23 / 29
SingleOptin
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 50.00% covered (danger) +
+
+
50.00%
1 / 2
4.14
+
+ 79.31% covered (warning) +
+
+
79.31%
23 / 29
 __construct
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.09
+
+ 78.57% covered (warning) +
+
+
78.57%
22 / 28
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7use Illuminate\Support\Str;
8use Kwaadpepper\CrudPolicies\Enums\CrudAction;
9use Kwaadpepper\CrudPolicies\Enums\CrudType;
10use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
11
12class SingleOptin extends Model
13{
14    use HasFactory;
15    use IsCrudModel;
16
17    /** @var string */
18    protected $crudLabelColumn = 'email';
19
20    /** @var string */
21    protected $crudValueColumn = 'id';
22
23    /**
24     * The attributes that are mass assignable.
25     *
26     * @var string[]
27     */
28    protected $fillable = [
29        'email'
30    ];
31
32    /**
33     * SingleOptin
34     *
35     * @param array $attributes
36     */
37    public function __construct(array $attributes = [])
38    {
39        self::$editableProperties = [
40            'email' => [
41                'label' => Str::ucfirst(trans('models.common.email')),
42                'type' => CrudType::email(),
43                'validate' => [
44                    'required',
45                    'email:rfc,dns',
46                    // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
47                    function ($attribute, $value, $fail) {
48                        $q = static::query();
49                        if (request()->user) {
50                            $q = $q->where('id', '!=', $this->id);
51                        }
52                        if ($q->where('email', $value)->exists()) {
53                            $fail(__(':attribute est déjà utilisé', [
54                                'attribute' => $attribute
55                            ]));
56                        }
57                    }
58                ],
59                'actions' => [
60                    CrudAction::viewAny(),
61                    CrudAction::view(),
62                    CrudAction::create(),
63                    CrudAction::update()
64                ]
65            ],
66            'brand_id' => [
67                'label' => Str::ucfirst(trans('models.classes.brand')),
68                'type' => CrudType::belongsTo(),
69                'belongsTo' => Brand::class,
70                'actions' => [
71                    CrudAction::viewAny(),
72                    CrudAction::view()
73                ]
74            ],
75            'created_at' => [
76                'label' => Str::ucfirst(trans('models.common.optinDate')),
77                'type' => CrudType::string(),
78                'readonly' => true,
79                'actions' => [
80                    CrudAction::viewAny(),
81                    CrudAction::view(),
82                    CrudAction::update()
83                ]
84            ],
85        ];
86        parent::__construct($attributes);
87    }
88
89    // * RELATIONS
90
91    /**
92     * Brand BelongsTo relation
93     *
94     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
95     */
96    public function brand()
97    {
98        return $this->belongsTo(Brand::class);
99    }
100}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/User.php.html b/tests/coverage/Models/User.php.html new file mode 100644 index 0000000..cf73285 --- /dev/null +++ b/tests/coverage/Models/User.php.html @@ -0,0 +1,144 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models/User.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
User
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Foundation\Auth\User as Authenticatable;
7use Illuminate\Notifications\Notifiable;
8
9class User extends Authenticatable
10{
11    use HasFactory;
12    use Notifiable;
13
14    /**
15     * The attributes that are mass assignable.
16     *
17     * @var array
18     */
19    protected $fillable = [
20        'name',
21        'email',
22        'password',
23    ];
24
25    /**
26     * The attributes that should be hidden for arrays.
27     *
28     * @var array
29     */
30    protected $hidden = [
31        'password',
32        'remember_token',
33    ];
34
35    /**
36     * The attributes that should be cast to native types.
37     *
38     * @var array
39     */
40    protected $casts = [
41        'email_verified_at' => 'datetime',
42    ];
43}
+ + + +
+ + + + + + diff --git a/tests/coverage/Models/dashboard.html b/tests/coverage/Models/dashboard.html new file mode 100644 index 0000000..09b348e --- /dev/null +++ b/tests/coverage/Models/dashboard.html @@ -0,0 +1,309 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Models + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + + +
ClassCoverage
App\Models\BrandStat50%
App\Models\Brand71%
App\Models\SingleOptin79%
App\Models\Service80%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + + +
ClassCRAP
App\Models\Service15
App\Models\SingleOptin4
App\Models\BrandStat4
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+
+

Project Risks

+
+ + + + + + + + + + + + + +
MethodCRAP
loadBrandData5
__construct3
booted2
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Models/index.html b/tests/coverage/Models/index.html new file mode 100644 index 0000000..f3ef0e8 --- /dev/null +++ b/tests/coverage/Models/index.html @@ -0,0 +1,440 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Models + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 86.77% covered (warning) +
+
+
86.77%
223 / 257
+
+ 69.64% covered (warning) +
+
+
69.64%
39 / 56
+
+ 50.00% covered (danger) +
+
+
50.00%
6 / 12
Answer.php
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Brand.php
+
+ 71.43% covered (warning) +
+
+
71.43%
20 / 28
+
+ 40.00% covered (danger) +
+
+
40.00%
4 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
BrandStat.php
+
+ 50.00% covered (danger) +
+
+
50.00%
3 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
Customer.php
+
+ 93.22% covered (success) +
+
+
93.22%
55 / 59
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CustomerAnswer.php
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CustomerInfo.php
+
+ 94.74% covered (success) +
+
+
94.74%
18 / 19
+
+ 80.00% covered (warning) +
+
+
80.00%
4 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CustomerLogin.php
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CustomerOptInHistory.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Question.php
+
+ 100.00% covered (success) +
+
+
100.00%
26 / 26
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Service.php
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
+
+ 60.00% covered (warning) +
+
+
60.00%
6 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ServiceStat.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
SingleOptin.php
+
+ 79.31% covered (warning) +
+
+
79.31%
23 / 29
+
+ 50.00% covered (danger) +
+
+
50.00%
1 / 2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
User.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Policies/AnswerPolicy.php.html b/tests/coverage/Policies/AnswerPolicy.php.html new file mode 100644 index 0000000..055e2d3 --- /dev/null +++ b/tests/coverage/Policies/AnswerPolicy.php.html @@ -0,0 +1,145 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Policies/AnswerPolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AnswerPolicy
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Policies\Rules\UserStaticRules as USR;
6use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
7
8/**
9 * @codeCoverageIgnore
10 */
11class AnswerPolicy extends RootPolicy
12{
13    /**
14     * Init policies on call
15     *
16     * @return void
17     */
18    public function setPolicies()
19    {
20        /**
21         * This first rule to return true in a policy action authorises the action
22         */
23        $this->policies = [
24            'answer' => [
25                'viewAny' => USR::make('everyOne'),
26                'view' => USR::make('noOne'),
27                'update' => USR::make('everyOne'),
28                'create' => USR::make('everyOne'),
29                'delete' => USR::make('everyOne')
30            ]
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/Policies/BrandPolicy.php.html b/tests/coverage/Policies/BrandPolicy.php.html new file mode 100644 index 0000000..a5ab02c --- /dev/null +++ b/tests/coverage/Policies/BrandPolicy.php.html @@ -0,0 +1,145 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Policies/BrandPolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
BrandPolicy
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Policies\Rules\UserStaticRules as USR;
6use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
7
8/**
9 * @codeCoverageIgnore
10 */
11class BrandPolicy extends RootPolicy
12{
13    /**
14     * Init policies on call
15     *
16     * @return void
17     */
18    public function setPolicies()
19    {
20        /**
21         * This first rule to return true in a policy action authorises the action
22         */
23        $this->policies = [
24            'brand' => [
25                'viewAny' => USR::make('everyOne'),
26                'view' => USR::make('noOne'),
27                'update' => USR::make('everyOne'),
28                'create' => USR::make('everyOne'),
29                'delete' => USR::make('noOne')
30            ]
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/Policies/CustomerPolicy.php.html b/tests/coverage/Policies/CustomerPolicy.php.html new file mode 100644 index 0000000..f7181b3 --- /dev/null +++ b/tests/coverage/Policies/CustomerPolicy.php.html @@ -0,0 +1,173 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Policies/CustomerPolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerPolicy
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
 customerBelongsToService
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Models\Customer;
6use App\Models\Service;
7use App\Policies\Rules\UserStaticRules as USR;
8use Illuminate\Auth\Access\HandlesAuthorization;
9use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
10
11/**
12 * @codeCoverageIgnore
13 */
14class CustomerPolicy extends RootPolicy
15{
16    use HandlesAuthorization;
17
18    /**
19     * Init policies on call
20     *
21     * @return void
22     */
23    public function setPolicies()
24    {
25        /**
26         * This first rule to return true in a policy action authorises the action
27         */
28        $this->policies = [
29            'customer' => [
30                'viewAny' => USR::make('everyOne'),
31                'view' => USR::make('everyOne'),
32                'create' => USR::make('noOne'),
33                'update' => USR::make('noOne'),
34                'delete' => USR::make('everyOne')
35            ]
36        ];
37    }
38
39    /**
40     * Check if the customer belongs to the service
41     *
42     * @param Service  $service
43     * @param Customer $customer
44     * @return boolean
45     */
46    public function customerBelongsToService(Service $service, Customer $customer)
47    {
48        return !\is_null($customer->login($service->brand));
49    }
50}
+ + + +
+ + + + + + diff --git a/tests/coverage/Policies/QuestionPolicy.php.html b/tests/coverage/Policies/QuestionPolicy.php.html new file mode 100644 index 0000000..cb11782 --- /dev/null +++ b/tests/coverage/Policies/QuestionPolicy.php.html @@ -0,0 +1,170 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Policies/QuestionPolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
QuestionPolicy
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
 questionBelongsToService
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Models\Question;
6use App\Models\Service;
7use App\Policies\Rules\UserStaticRules as USR;
8use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
9
10/**
11 * @codeCoverageIgnore
12 */
13class QuestionPolicy extends RootPolicy
14{
15    /**
16     * Init policies on call
17     *
18     * @return void
19     */
20    public function setPolicies()
21    {
22        /**
23         * This first rule to return true in a policy action authorises the action
24         */
25        $this->policies = [
26            'question' => [
27                'viewAny' => USR::make('everyOne'),
28                'view' => USR::make('everyOne'),
29                'update' => USR::make('everyOne'),
30                'create' => USR::make('everyOne'),
31                'delete' => USR::make('everyOne')
32            ]
33        ];
34    }
35
36    /**
37     * Check if the question belongs to the service
38     *
39     * @param Service  $service
40     * @param Question $question
41     * @return boolean
42     */
43    public function questionBelongsToService(Service $service, Question $question)
44    {
45        return $service->brand->id === $question->brand_id;
46    }
47}
+ + + +
+ + + + + + diff --git a/tests/coverage/Policies/Rules/UserStaticRules.php.html b/tests/coverage/Policies/Rules/UserStaticRules.php.html new file mode 100644 index 0000000..8a5d6ad --- /dev/null +++ b/tests/coverage/Policies/Rules/UserStaticRules.php.html @@ -0,0 +1,156 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Policies/Rules/UserStaticRules.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
UserStaticRules
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 everyOne
n/a
0 / 0
1
n/a
0 / 0
 noOne
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies\Rules;
4
5use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
6
7/**
8 * Userlevel Policiy Static Rules
9 * ! Rules has to return boolean value
10 * @codeCoverageIgnore
11 */
12abstract class UserStaticRules extends StaticRules
13{
14    /**
15     * Allow everyone
16     *
17     * @return boolean
18     */
19    protected static function everyOne(): bool
20    {
21        return true;
22    }
23    /**
24     * Allow no one
25     *
26     * @return boolean
27     */
28    protected static function noOne(): bool
29    {
30        return false;
31    }
32}
+ + + +
+ + + + + + diff --git a/tests/coverage/Policies/Rules/dashboard.html b/tests/coverage/Policies/Rules/dashboard.html new file mode 100644 index 0000000..005089b --- /dev/null +++ b/tests/coverage/Policies/Rules/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Policies/Rules + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Policies/Rules/index.html b/tests/coverage/Policies/Rules/index.html new file mode 100644 index 0000000..87254b4 --- /dev/null +++ b/tests/coverage/Policies/Rules/index.html @@ -0,0 +1,90 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Policies/Rules + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
UserStaticRules.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Policies/ServicePolicy.php.html b/tests/coverage/Policies/ServicePolicy.php.html new file mode 100644 index 0000000..51803ec --- /dev/null +++ b/tests/coverage/Policies/ServicePolicy.php.html @@ -0,0 +1,145 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Policies/ServicePolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ServicePolicy
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Policies\Rules\UserStaticRules as USR;
6use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
7
8/**
9 * @codeCoverageIgnore
10 */
11class ServicePolicy extends RootPolicy
12{
13    /**
14     * Init policies on call
15     *
16     * @return void
17     */
18    public function setPolicies()
19    {
20        /**
21         * This first rule to return true in a policy action authorises the action
22         */
23        $this->policies = [
24            'service' => [
25                'viewAny' => USR::make('everyOne'),
26                'view' => USR::make('everyOne'),
27                'update' => USR::make('everyOne'),
28                'create' => USR::make('everyOne'),
29                'delete' => USR::make('noOne')
30            ]
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/Policies/SingleOptinPolicy.php.html b/tests/coverage/Policies/SingleOptinPolicy.php.html new file mode 100644 index 0000000..a8e5cc7 --- /dev/null +++ b/tests/coverage/Policies/SingleOptinPolicy.php.html @@ -0,0 +1,145 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Policies/SingleOptinPolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
SingleOptinPolicy
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Policies\Rules\UserStaticRules as USR;
6use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
7
8/**
9 * @codeCoverageIgnore
10 */
11class SingleOptinPolicy extends RootPolicy
12{
13    /**
14     * Init policies on call
15     *
16     * @return void
17     */
18    public function setPolicies()
19    {
20        /**
21         * This first rule to return true in a policy action authorises the action
22         */
23        $this->policies = [
24            'singleoptin' => [
25                'viewAny' => USR::make('everyOne'),
26                'view' => USR::make('noOne'),
27                'create' => USR::make('noOne'),
28                'update' => USR::make('noOne'),
29                'delete' => USR::make('everyOne')
30            ]
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/Policies/dashboard.html b/tests/coverage/Policies/dashboard.html new file mode 100644 index 0000000..90ac9b9 --- /dev/null +++ b/tests/coverage/Policies/dashboard.html @@ -0,0 +1,284 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Policies + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Policies/index.html b/tests/coverage/Policies/index.html new file mode 100644 index 0000000..7c670b5 --- /dev/null +++ b/tests/coverage/Policies/index.html @@ -0,0 +1,167 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Policies + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Rules
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
AnswerPolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
BrandPolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
CustomerPolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
QuestionPolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ServicePolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
SingleOptinPolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/Providers/AppServiceProvider.php.html b/tests/coverage/Providers/AppServiceProvider.php.html new file mode 100644 index 0000000..b1fee54 --- /dev/null +++ b/tests/coverage/Providers/AppServiceProvider.php.html @@ -0,0 +1,184 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Providers/AppServiceProvider.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AppServiceProvider
n/a
0 / 0
n/a
0 / 0
4
n/a
0 / 0
 boot
n/a
0 / 0
4
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Providers;
4
5use Illuminate\Cache\RateLimiting\Limit;
6use Illuminate\Http\Resources\Json\JsonResource;
7use Illuminate\Pagination\Paginator;
8use Illuminate\Support\Carbon;
9use Illuminate\Support\Facades\App;
10use Illuminate\Support\Facades\Cache;
11use Illuminate\Support\Facades\File;
12use Illuminate\Support\Facades\RateLimiter;
13use Illuminate\Support\Facades\URL;
14use Illuminate\Support\Facades\View;
15use Illuminate\Support\ServiceProvider;
16
17/**
18 * @codeCoverageIgnore
19 */
20class AppServiceProvider extends ServiceProvider
21{
22    /**
23     * Bootstrap any application services.
24     *
25     * @return void
26     */
27    public function boot()
28    {
29        // * STATS jobs Rate Limiting.
30        RateLimiter::for('stats', function () {
31            return Limit::perMinute(60)->by(2);
32        });
33
34        // ! Important pourra etre retiré quand la V1 sera supprimé
35        // empeche l'encapsulation dans data
36        JsonResource::withoutWrapping();
37
38        // * LOCAL HTTPS FIX.
39        if (!in_array(config('app.env'), ['local', 'testing'])) {
40            URL::forceScheme('https');
41        }
42        // * HA PROXY FIX.
43        App::singleton('clientIp', function () {
44            if (
45                config('app.env') === 'local'
46            ) {
47                return request()->getClientIp();
48            } else {
49                return request()->server('HTTP_X_FORWARDED_FOR') ??
50                request()->getClientIp();
51            }
52        });
53        // * LOCAL FORCE eg: for Carbon.
54        setlocale(LC_ALL, \sprintf(
55            '%s_%s.UTF-8',
56            config('app.locale'),
57            \strtoupper(config('app.locale'))
58        ));
59        Carbon::setLocale(config('app.locale'));
60        // * FORCE BOOTSTRAP PAGINATOR.
61        if (!app()->runningInConsole()) {
62            Paginator::useBootstrap();
63        }
64
65        $appInfos = Cache::remember('composer', 360, function () {
66            return json_decode(File::get(\app_path('../composer.json')));
67        });
68        View::share('name', $appInfos->name);
69        View::share('version', $appInfos->version);
70        View::share('license', $appInfos->license);
71    }
72}
+ + + +
+ + + + + + diff --git a/tests/coverage/Providers/AuthServiceProvider.php.html b/tests/coverage/Providers/AuthServiceProvider.php.html new file mode 100644 index 0000000..436d2f8 --- /dev/null +++ b/tests/coverage/Providers/AuthServiceProvider.php.html @@ -0,0 +1,228 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Providers/AuthServiceProvider.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
AuthServiceProvider
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
 boot
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Providers;
4
5use App\Lib\Helper;
6use App\Models\Service;
7use Illuminate\Database\Eloquent\ModelNotFoundException;
8use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
9use Illuminate\Http\Request;
10use Illuminate\Support\Facades\Auth;
11
12class AuthServiceProvider extends ServiceProvider
13{
14    /**
15     * The policy mappings for the application.
16     *
17     * @var array
18     */
19    protected $policies = [];
20
21    /**
22     * Register any authentication / authorization services.
23     *
24     * @return void
25     */
26    public function boot()
27    {
28        $this->registerPolicies();
29
30        Auth::viaRequest('api-hmac', function (Request $request) {
31
32            /**
33             * Un token devrais être fourni pour chaque requete
34             */
35            if (is_null($request->bearer_token)) {
36                return null;
37            }
38
39            // * Si la route actuelle n'as pas de nom alors on refuse de la traiter.
40            if (!(Helper::getRoute($request))) {
41                // @codeCoverageIgnoreStart
42                return null;
43                // @codeCoverageIgnoreEnd
44            }
45
46            $service = null;
47
48            try {
49                // * On récupère le service actuel correspondant au token fourni.
50                $service = Service::getFromBearerToken($request->bearer_token ?? '');
51            } catch (ModelNotFoundException $e) {
52                return null;
53            }
54
55            // * Preload the brand as we will access it often later.
56            $service = $service->load('brand');
57
58            /**
59             ** All ApiV2 routes must pass Brand model to controller action since a Brand owns
60             ** a service token, otherwise the api cannot be called.
61             ** This hack is a custom binder instead of passing
62             ** this to route as a parameter
63             */
64            app()->bind(Service::class, function () use ($service) {
65                return $service;
66            });
67
68            // * On authenticate la request en vérifiant sa signature.
69            if ($service->authenticateApi($request)) {
70                return $service;
71            }
72
73            return null;
74        });
75    }
76}
+ + + +
+ + + + + + diff --git a/tests/coverage/Providers/EventServiceProvider.php.html b/tests/coverage/Providers/EventServiceProvider.php.html new file mode 100644 index 0000000..e2ae889 --- /dev/null +++ b/tests/coverage/Providers/EventServiceProvider.php.html @@ -0,0 +1,125 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Providers/EventServiceProvider.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
EventServiceProvider
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Providers;
4
5use Illuminate\Auth\Events\Registered;
6use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
7use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
8
9/**
10 * codeCoverageIgnore
11 */
12class EventServiceProvider extends ServiceProvider
13{
14    /**
15     * The event listener mappings for the application.
16     *
17     * @var array
18     */
19    protected $listen = [
20        Registered::class => [
21            SendEmailVerificationNotification::class,
22        ],
23    ];
24}
+ + + +
+ + + + + + diff --git a/tests/coverage/Providers/RouteServiceProvider.php.html b/tests/coverage/Providers/RouteServiceProvider.php.html new file mode 100644 index 0000000..6aa4922 --- /dev/null +++ b/tests/coverage/Providers/RouteServiceProvider.php.html @@ -0,0 +1,280 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Providers/RouteServiceProvider.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
33 / 33
RouteServiceProvider
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
7
+
+ 100.00% covered (success) +
+
+
100.00%
33 / 33
 boot
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
26 / 26
 configureRateLimiting
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Providers;
4
5use App\Models\Answer;
6use App\Models\Brand;
7use App\Models\Customer;
8use App\Models\CustomerInfo;
9use App\Models\Question;
10use App\Models\Service;
11use App\Models\SingleOptin;
12use Illuminate\Cache\RateLimiting\Limit;
13use Illuminate\Database\Eloquent\ModelNotFoundException;
14use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
15use Illuminate\Http\Request;
16use Illuminate\Support\Facades\RateLimiter;
17use Illuminate\Support\Facades\Route;
18
19class RouteServiceProvider extends ServiceProvider
20{
21    /**
22     * The path to the "home" route for your application.
23     *
24     * This is used by Laravel authentication to redirect users after login.
25     *
26     * @var string
27     */
28    public const HOME = '/customers';
29
30    /**
31     * Define your route model bindings, pattern filters, etc.
32     *
33     * @return void
34     */
35    public function boot()
36    {
37        $this->configureRateLimiting();
38
39        $this->routes(function () {
40            // @codeCoverageIgnoreStart
41            Route::prefix('api')->middleware('api')->namespace($this->namespace)->group(base_path('routes/api.php'));
42            Route::middleware('web')->namespace($this->namespace)->group(base_path('routes/web.php'));
43            // @codeCoverageIgnoreEnd
44        });
45
46        Route::model('brand', Brand::class);
47        Route::model('service', Service::class);
48        Route::model('single_optin', SingleOptin::class);
49        Route::model('question', Question::class);
50        Route::model('answer', Answer::class);
51        Route::model('customer', Customer::class);
52
53        // * Api parameters Bind.
54        Route::model('serviceQuestion', Question::class);
55        Route::bind('serviceCustomer', function ($value) {
56            // * Load brand
57            /** @var \App\Models\Brand $brand */
58            $brand = app(Service::class)->brand;
59
60            // ? Parameter is email
61            if (strpos($value, '@') !== false) {
62                $customer = Customer::whereHas('brands', function ($q) use ($brand) {
63                    $q->where('brands.id', '=', $brand->id);
64                })->where('email', '=', $value)->firstOrFail();
65            }
66
67            try {
68                // ? Have we found a customer yet
69                if (!isset($customer) or \get_class($customer) !== Customer::class) {
70                    // ? Parameter is hashcode
71                    $customer = CustomerInfo::whereHas('brand', function ($q) use ($brand) {
72                        $q->where('brands.id', '=', $brand->id);
73                    })->where('hashcode', $value)
74                        ->with('customer')->firstOrFail()->customer;
75                }
76
77                // * Load Customer Login and Info for this brand.
78                /** @var \App\Models\Customer $customer */
79
80                return $customer->loadBrandData($brand);
81            } catch (ModelNotFoundException $e) {
82                // * Uniform Not found exception using model Customer.
83                throw (new ModelNotFoundException())->setModel(Customer::class);
84            }
85        });
86    }
87
88    /**
89     * Configure the rate limiters for the application.
90     *
91     * @return void
92     */
93    protected function configureRateLimiting()
94    {
95        RateLimiter::for('web', function (Request $request) {
96            // @codeCoverageIgnoreStart
97            return Limit::perMinute(25)
98                ->by(optional($request->user())->id ?: app('clientIp'));
99            // @codeCoverageIgnoreEnd
100        });
101        // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
102        RateLimiter::for('api', function (Request $request) {
103            return Limit::perMinute(config('api.apiRequestRatePerMinute', 100))
104            ->by(app('clientIp'));
105        });
106    }
107}
+ + + +
+ + + + + + diff --git a/tests/coverage/Providers/dashboard.html b/tests/coverage/Providers/dashboard.html new file mode 100644 index 0000000..adc3b67 --- /dev/null +++ b/tests/coverage/Providers/dashboard.html @@ -0,0 +1,284 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app/Providers + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/Providers/index.html b/tests/coverage/Providers/index.html new file mode 100644 index 0000000..c181aec --- /dev/null +++ b/tests/coverage/Providers/index.html @@ -0,0 +1,173 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app/Providers + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
51 / 51
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
AppServiceProvider.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
AuthServiceProvider.php
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
EventServiceProvider.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
RouteServiceProvider.php
+
+ 100.00% covered (success) +
+
+
100.00%
33 / 33
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/_css/bootstrap.min.css b/tests/coverage/_css/bootstrap.min.css new file mode 100644 index 0000000..7d2a868 --- /dev/null +++ b/tests/coverage/_css/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.5.0 (https://getbootstrap.com/) + * Copyright 2011-2020 The Bootstrap Authors + * Copyright 2011-2020 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item{display:-ms-flexbox;display:flex}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;-ms-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/tests/coverage/_css/custom.css b/tests/coverage/_css/custom.css new file mode 100644 index 0000000..e69de29 diff --git a/tests/coverage/_css/nv.d3.min.css b/tests/coverage/_css/nv.d3.min.css new file mode 100644 index 0000000..7a6f7fe --- /dev/null +++ b/tests/coverage/_css/nv.d3.min.css @@ -0,0 +1 @@ +.nvd3 .nv-axis{pointer-events:none;opacity:1}.nvd3 .nv-axis path{fill:none;stroke:#000;stroke-opacity:.75;shape-rendering:crispEdges}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{fill:none;stroke:#e5e5e5;shape-rendering:crispEdges}.nvd3 .nv-axis .zero line,.nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nvd3 .nv-axis.nv-disabled{opacity:0}.nvd3 .nv-bars rect{fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:rgba(0,0,0,0)}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect,.nvd3 .nv-discretebar .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,.nvd3 .nv-candlestickBar .nv-ticks rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:rgba(0,0,0,0)}.nvd3 .nv-boxplot circle{fill-opacity:.5}.nvd3 .nv-boxplot circle:hover{fill-opacity:1}.nvd3 .nv-boxplot rect:hover{fill-opacity:1}.nvd3 line.nv-boxplot-median{stroke:#000}.nv-boxplot-tick:hover{stroke-width:2.5px}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-candlestickBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect{stroke:#d62728;fill:#d62728}.with-transitions .nv-candlestickBar .nv-ticks .nv-tick{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-candlestickBar .nv-ticks line{stroke:#333}.nvd3 .nv-legend .nv-disabled rect{}.nvd3 .nv-check-box .nv-box{fill-opacity:0;stroke-width:2}.nvd3 .nv-check-box .nv-check{fill-opacity:0;stroke-width:4}.nvd3 .nv-series.nv-disabled .nv-check-box .nv-check{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check{opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3 .nv-groups path.nv-line{fill:none}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-scatter .nv-groups .nv-point.hover,.nvd3 .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}svg.nvd3-svg{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-ms-user-select:none;-moz-user-select:none;user-select:none;display:block;width:100%;height:100%}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nvd3 text{font:400 12px Arial}.nvd3 .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .nv-disabled circle{fill-opacity:0}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3 .background path{fill:none;stroke:#EEE;stroke-opacity:.4;shape-rendering:crispEdges}.nvd3 .foreground path{fill:none;stroke-opacity:.7}.nvd3 .nv-parallelCoordinates-brush .extent{fill:#fff;fill-opacity:.6;stroke:gray;shape-rendering:crispEdges}.nvd3 .nv-parallelCoordinates .hover{fill-opacity:1;stroke-width:3px}.nvd3 .missingValuesline line{fill:none;stroke:#000;stroke-width:1;stroke-opacity:1;stroke-dasharray:5,5}.nvd3.nv-pie path{stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-pie .nv-pie-title{font-size:24px;fill:rgba(19,196,249,.59)}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label{pointer-events:none}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nv-noninteractive{pointer-events:none}.nv-distx,.nv-disty{pointer-events:none}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvtooltip{position:absolute;background-color:rgba(255,255,255,1);color:rgba(0,0,0,1);padding:1px;border:1px solid rgba(0,0,0,.2);z-index:10000;display:block;font-family:Arial;font-size:13px;text-align:left;pointer-events:none;white-space:nowrap;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nvtooltip{background:rgba(255,255,255,.8);border:1px solid rgba(0,0,0,.5);border-radius:4px}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 50ms linear;-moz-transition:opacity 50ms linear;-webkit-transition:opacity 50ms linear;transition-delay:200ms;-moz-transition-delay:200ms;-webkit-transition-delay:200ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{margin:0;padding:4px 14px;line-height:18px;font-weight:400;background-color:rgba(247,247,247,.75);color:rgba(0,0,0,1);text-align:center;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.nvtooltip p{margin:0;padding:5px 14px;text-align:center}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key{font-weight:400}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{width:8px;height:8px;vertical-align:middle}.nvtooltip table td.legend-color-guide div{width:12px;height:12px;border:1px solid #999}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{pointer-events:none;display:none}.nvd3 .nv-interactiveGuideLine{pointer-events:none}.nvd3 line.nv-guideline{stroke:#ccc} \ No newline at end of file diff --git a/tests/coverage/_css/octicons.css b/tests/coverage/_css/octicons.css new file mode 100644 index 0000000..31d9786 --- /dev/null +++ b/tests/coverage/_css/octicons.css @@ -0,0 +1,5 @@ +.octicon { + display: inline-block; + vertical-align: text-top; + fill: currentColor; +} diff --git a/tests/coverage/_css/style.css b/tests/coverage/_css/style.css new file mode 100644 index 0000000..088e9f7 --- /dev/null +++ b/tests/coverage/_css/style.css @@ -0,0 +1,127 @@ +body { + padding-top: 10px; +} + +.popover { + max-width: none; +} + +.octicon { + margin-right:.25em; +} + +.table-bordered>thead>tr>td { + border-bottom-width: 1px; +} + +.table tbody>tr>td, .table thead>tr>td { + padding-top: 3px; + padding-bottom: 3px; +} + +.table-condensed tbody>tr>td { + padding-top: 0; + padding-bottom: 0; +} + +.table .progress { + margin-bottom: inherit; +} + +.table-borderless th, .table-borderless td { + border: 0 !important; +} + +.table tbody tr.covered-by-large-tests, li.covered-by-large-tests, tr.success, td.success, li.success, span.success { + background-color: #dff0d8; +} + +.table tbody tr.covered-by-medium-tests, li.covered-by-medium-tests { + background-color: #c3e3b5; +} + +.table tbody tr.covered-by-small-tests, li.covered-by-small-tests { + background-color: #99cb84; +} + +.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger { + background-color: #f2dede; +} + +.table tbody tr.warning, .table tbody td.warning, li.warning, span.warning { + background-color: #fcf8e3; +} + +.table tbody td.info { + background-color: #d9edf7; +} + +td.big { + width: 117px; +} + +td.small { +} + +td.codeLine { + font-family: "Source Code Pro", "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + white-space: pre-wrap; +} + +td span.comment { + color: #888a85; +} + +td span.default { + color: #2e3436; +} + +td span.html { + color: #888a85; +} + +td span.keyword { + color: #2e3436; + font-weight: bold; +} + +pre span.string { + color: #2e3436; +} + +span.success, span.warning, span.danger { + margin-right: 2px; + padding-left: 10px; + padding-right: 10px; + text-align: center; +} + +#classCoverageDistribution, #classComplexity { + height: 200px; + width: 475px; +} + +#toplink { + position: fixed; + left: 5px; + bottom: 5px; + outline: 0; +} + +svg text { + font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif; + font-size: 11px; + color: #666; + fill: #666; +} + +.scrollbox { + height:245px; + overflow-x:hidden; + overflow-y:scroll; +} + +table + .structure-heading { + border-top: 1px solid lightgrey; + padding-top: 0.5em; +} diff --git a/tests/coverage/_icons/file-code.svg b/tests/coverage/_icons/file-code.svg new file mode 100644 index 0000000..5b4b199 --- /dev/null +++ b/tests/coverage/_icons/file-code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/coverage/_icons/file-directory.svg b/tests/coverage/_icons/file-directory.svg new file mode 100644 index 0000000..4bf1f1c --- /dev/null +++ b/tests/coverage/_icons/file-directory.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/coverage/_js/bootstrap.min.js b/tests/coverage/_js/bootstrap.min.js new file mode 100644 index 0000000..3ecf55f --- /dev/null +++ b/tests/coverage/_js/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.5.0 (https://getbootstrap.com/) + * Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t=t||self).bootstrap={},t.jQuery,t.Popper)}(this,(function(t,e,n){"use strict";function i(t,e){for(var n=0;n=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};c.jQueryDetection(),e.fn.emulateTransitionEnd=l,e.event.special[c.TRANSITION_END]={bindType:"transitionend",delegateType:"transitionend",handle:function(t){if(e(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var h="alert",u=e.fn[h],d=function(){function t(t){this._element=t}var n=t.prototype;return n.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},n.dispose=function(){e.removeData(this._element,"bs.alert"),this._element=null},n._getRootElement=function(t){var n=c.getSelectorFromElement(t),i=!1;return n&&(i=document.querySelector(n)),i||(i=e(t).closest(".alert")[0]),i},n._triggerCloseEvent=function(t){var n=e.Event("close.bs.alert");return e(t).trigger(n),n},n._removeElement=function(t){var n=this;if(e(t).removeClass("show"),e(t).hasClass("fade")){var i=c.getTransitionDurationFromElement(t);e(t).one(c.TRANSITION_END,(function(e){return n._destroyElement(t,e)})).emulateTransitionEnd(i)}else this._destroyElement(t)},n._destroyElement=function(t){e(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.alert");o||(o=new t(this),i.data("bs.alert",o)),"close"===n&&o[n](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}}]),t}();e(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',d._handleDismiss(new d)),e.fn[h]=d._jQueryInterface,e.fn[h].Constructor=d,e.fn[h].noConflict=function(){return e.fn[h]=u,d._jQueryInterface};var f=e.fn.button,g=function(){function t(t){this._element=t}var n=t.prototype;return n.toggle=function(){var t=!0,n=!0,i=e(this._element).closest('[data-toggle="buttons"]')[0];if(i){var o=this._element.querySelector('input:not([type="hidden"])');if(o){if("radio"===o.type)if(o.checked&&this._element.classList.contains("active"))t=!1;else{var s=i.querySelector(".active");s&&e(s).removeClass("active")}t&&("checkbox"!==o.type&&"radio"!==o.type||(o.checked=!this._element.classList.contains("active")),e(o).trigger("change")),o.focus(),n=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(n&&this._element.setAttribute("aria-pressed",!this._element.classList.contains("active")),t&&e(this._element).toggleClass("active"))},n.dispose=function(){e.removeData(this._element,"bs.button"),this._element=null},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.button");i||(i=new t(this),e(this).data("bs.button",i)),"toggle"===n&&i[n]()}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}}]),t}();e(document).on("click.bs.button.data-api",'[data-toggle^="button"]',(function(t){var n=t.target,i=n;if(e(n).hasClass("btn")||(n=e(n).closest(".btn")[0]),!n||n.hasAttribute("disabled")||n.classList.contains("disabled"))t.preventDefault();else{var o=n.querySelector('input:not([type="hidden"])');if(o&&(o.hasAttribute("disabled")||o.classList.contains("disabled")))return void t.preventDefault();"LABEL"===i.tagName&&o&&"checkbox"===o.type&&t.preventDefault(),g._jQueryInterface.call(e(n),"toggle")}})).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',(function(t){var n=e(t.target).closest(".btn")[0];e(n).toggleClass("focus",/^focus(in)?$/.test(t.type))})),e(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var n=t.prototype;return n.next=function(){this._isSliding||this._slide("next")},n.nextWhenVisible=function(){!document.hidden&&e(this._element).is(":visible")&&"hidden"!==e(this._element).css("visibility")&&this.next()},n.prev=function(){this._isSliding||this._slide("prev")},n.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(c.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},n.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},n.to=function(t){var n=this;this._activeElement=this._element.querySelector(".active.carousel-item");var i=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)e(this._element).one("slid.bs.carousel",(function(){return n.to(t)}));else{if(i===t)return this.pause(),void this.cycle();var o=t>i?"next":"prev";this._slide(o,this._items[t])}},n.dispose=function(){e(this._element).off(p),e.removeData(this._element,"bs.carousel"),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},n._getConfig=function(t){return t=a(a({},v),t),c.typeCheckConfig(m,t,b),t},n._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},n._addEventListeners=function(){var t=this;this._config.keyboard&&e(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&e(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},n._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var n=function(e){t._pointerEvent&&y[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},i=function(e){t._pointerEvent&&y[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};e(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(e(this._element).on("pointerdown.bs.carousel",(function(t){return n(t)})),e(this._element).on("pointerup.bs.carousel",(function(t){return i(t)})),this._element.classList.add("pointer-event")):(e(this._element).on("touchstart.bs.carousel",(function(t){return n(t)})),e(this._element).on("touchmove.bs.carousel",(function(e){return function(e){e.originalEvent.touches&&e.originalEvent.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),e(this._element).on("touchend.bs.carousel",(function(t){return i(t)})))}},n._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},n._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},n._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),s=this._items.length-1;if((i&&0===o||n&&o===s)&&!this._config.wrap)return e;var r=(o+("prev"===t?-1:1))%this._items.length;return-1===r?this._items[this._items.length-1]:this._items[r]},n._triggerSlideEvent=function(t,n){var i=this._getItemIndex(t),o=this._getItemIndex(this._element.querySelector(".active.carousel-item")),s=e.Event("slide.bs.carousel",{relatedTarget:t,direction:n,from:o,to:i});return e(this._element).trigger(s),s},n._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var n=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));e(n).removeClass("active");var i=this._indicatorsElement.children[this._getItemIndex(t)];i&&e(i).addClass("active")}},n._slide=function(t,n){var i,o,s,r=this,a=this._element.querySelector(".active.carousel-item"),l=this._getItemIndex(a),h=n||a&&this._getItemByDirection(t,a),u=this._getItemIndex(h),d=Boolean(this._interval);if("next"===t?(i="carousel-item-left",o="carousel-item-next",s="left"):(i="carousel-item-right",o="carousel-item-prev",s="right"),h&&e(h).hasClass("active"))this._isSliding=!1;else if(!this._triggerSlideEvent(h,s).isDefaultPrevented()&&a&&h){this._isSliding=!0,d&&this.pause(),this._setActiveIndicatorElement(h);var f=e.Event("slid.bs.carousel",{relatedTarget:h,direction:s,from:l,to:u});if(e(this._element).hasClass("slide")){e(h).addClass(o),c.reflow(h),e(a).addClass(i),e(h).addClass(i);var g=parseInt(h.getAttribute("data-interval"),10);g?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=g):this._config.interval=this._config.defaultInterval||this._config.interval;var m=c.getTransitionDurationFromElement(a);e(a).one(c.TRANSITION_END,(function(){e(h).removeClass(i+" "+o).addClass("active"),e(a).removeClass("active "+o+" "+i),r._isSliding=!1,setTimeout((function(){return e(r._element).trigger(f)}),0)})).emulateTransitionEnd(m)}else e(a).removeClass("active"),e(h).addClass("active"),this._isSliding=!1,e(this._element).trigger(f);d&&this.cycle()}},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.carousel"),o=a(a({},v),e(this).data());"object"==typeof n&&(o=a(a({},o),n));var s="string"==typeof n?n:o.slide;if(i||(i=new t(this,o),e(this).data("bs.carousel",i)),"number"==typeof n)i.to(n);else if("string"==typeof s){if("undefined"==typeof i[s])throw new TypeError('No method named "'+s+'"');i[s]()}else o.interval&&o.ride&&(i.pause(),i.cycle())}))},t._dataApiClickHandler=function(n){var i=c.getSelectorFromElement(this);if(i){var o=e(i)[0];if(o&&e(o).hasClass("carousel")){var s=a(a({},e(o).data()),e(this).data()),r=this.getAttribute("data-slide-to");r&&(s.interval=!1),t._jQueryInterface.call(e(o),s),r&&e(o).data("bs.carousel").to(r),n.preventDefault()}}},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"Default",get:function(){return v}}]),t}();e(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",E._dataApiClickHandler),e(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),n=0,i=t.length;n0&&(this._selector=r,this._triggerArray.push(s))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var n=t.prototype;return n.toggle=function(){e(this._element).hasClass("show")?this.hide():this.show()},n.show=function(){var n,i,o=this;if(!this._isTransitioning&&!e(this._element).hasClass("show")&&(this._parent&&0===(n=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof o._config.parent?t.getAttribute("data-parent")===o._config.parent:t.classList.contains("collapse")}))).length&&(n=null),!(n&&(i=e(n).not(this._selector).data("bs.collapse"))&&i._isTransitioning))){var s=e.Event("show.bs.collapse");if(e(this._element).trigger(s),!s.isDefaultPrevented()){n&&(t._jQueryInterface.call(e(n).not(this._selector),"hide"),i||e(n).data("bs.collapse",null));var r=this._getDimension();e(this._element).removeClass("collapse").addClass("collapsing"),this._element.style[r]=0,this._triggerArray.length&&e(this._triggerArray).removeClass("collapsed").attr("aria-expanded",!0),this.setTransitioning(!0);var a="scroll"+(r[0].toUpperCase()+r.slice(1)),l=c.getTransitionDurationFromElement(this._element);e(this._element).one(c.TRANSITION_END,(function(){e(o._element).removeClass("collapsing").addClass("collapse show"),o._element.style[r]="",o.setTransitioning(!1),e(o._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(l),this._element.style[r]=this._element[a]+"px"}}},n.hide=function(){var t=this;if(!this._isTransitioning&&e(this._element).hasClass("show")){var n=e.Event("hide.bs.collapse");if(e(this._element).trigger(n),!n.isDefaultPrevented()){var i=this._getDimension();this._element.style[i]=this._element.getBoundingClientRect()[i]+"px",c.reflow(this._element),e(this._element).addClass("collapsing").removeClass("collapse show");var o=this._triggerArray.length;if(o>0)for(var s=0;s0},i._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=a(a({},e.offsets),t._config.offset(e.offsets,t._element)||{}),e}:e.offset=this._config.offset,e},i._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),a(a({},t),this._config.popperConfig)},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.dropdown");if(i||(i=new t(this,"object"==typeof n?n:null),e(this).data("bs.dropdown",i)),"string"==typeof n){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},t._clearMenus=function(n){if(!n||3!==n.which&&("keyup"!==n.type||9===n.which))for(var i=[].slice.call(document.querySelectorAll('[data-toggle="dropdown"]')),o=0,s=i.length;o0&&r--,40===n.which&&rdocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},n._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},n._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:F,popperConfig:null},Y={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},$=function(){function t(t,e){if("undefined"==typeof n)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var i=t.prototype;return i.enable=function(){this._isEnabled=!0},i.disable=function(){this._isEnabled=!1},i.toggleEnabled=function(){this._isEnabled=!this._isEnabled},i.toggle=function(t){if(this._isEnabled)if(t){var n=this.constructor.DATA_KEY,i=e(t.currentTarget).data(n);i||(i=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(n,i)),i._activeTrigger.click=!i._activeTrigger.click,i._isWithActiveTrigger()?i._enter(null,i):i._leave(null,i)}else{if(e(this.getTipElement()).hasClass("show"))return void this._leave(null,this);this._enter(null,this)}},i.dispose=function(){clearTimeout(this._timeout),e.removeData(this.element,this.constructor.DATA_KEY),e(this.element).off(this.constructor.EVENT_KEY),e(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&e(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},i.show=function(){var t=this;if("none"===e(this.element).css("display"))throw new Error("Please use show on visible elements");var i=e.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){e(this.element).trigger(i);var o=c.findShadowRoot(this.element),s=e.contains(null!==o?o:this.element.ownerDocument.documentElement,this.element);if(i.isDefaultPrevented()||!s)return;var r=this.getTipElement(),a=c.getUID(this.constructor.NAME);r.setAttribute("id",a),this.element.setAttribute("aria-describedby",a),this.setContent(),this.config.animation&&e(r).addClass("fade");var l="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,h=this._getAttachment(l);this.addAttachmentClass(h);var u=this._getContainer();e(r).data(this.constructor.DATA_KEY,this),e.contains(this.element.ownerDocument.documentElement,this.tip)||e(r).appendTo(u),e(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new n(this.element,r,this._getPopperConfig(h)),e(r).addClass("show"),"ontouchstart"in document.documentElement&&e(document.body).children().on("mouseover",null,e.noop);var d=function(){t.config.animation&&t._fixTransition();var n=t._hoverState;t._hoverState=null,e(t.element).trigger(t.constructor.Event.SHOWN),"out"===n&&t._leave(null,t)};if(e(this.tip).hasClass("fade")){var f=c.getTransitionDurationFromElement(this.tip);e(this.tip).one(c.TRANSITION_END,d).emulateTransitionEnd(f)}else d()}},i.hide=function(t){var n=this,i=this.getTipElement(),o=e.Event(this.constructor.Event.HIDE),s=function(){"show"!==n._hoverState&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),e(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()};if(e(this.element).trigger(o),!o.isDefaultPrevented()){if(e(i).removeClass("show"),"ontouchstart"in document.documentElement&&e(document.body).children().off("mouseover",null,e.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,e(this.tip).hasClass("fade")){var r=c.getTransitionDurationFromElement(i);e(i).one(c.TRANSITION_END,s).emulateTransitionEnd(r)}else s();this._hoverState=""}},i.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},i.isWithContent=function(){return Boolean(this.getTitle())},i.addAttachmentClass=function(t){e(this.getTipElement()).addClass("bs-tooltip-"+t)},i.getTipElement=function(){return this.tip=this.tip||e(this.config.template)[0],this.tip},i.setContent=function(){var t=this.getTipElement();this.setElementContent(e(t.querySelectorAll(".tooltip-inner")),this.getTitle()),e(t).removeClass("fade show")},i.setElementContent=function(t,n){"object"!=typeof n||!n.nodeType&&!n.jquery?this.config.html?(this.config.sanitize&&(n=H(n,this.config.whiteList,this.config.sanitizeFn)),t.html(n)):t.text(n):this.config.html?e(n).parent().is(t)||t.empty().append(n):t.text(e(n).text())},i.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},i._getPopperConfig=function(t){var e=this;return a(a({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),this.config.popperConfig)},i._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=a(a({},e.offsets),t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},i._getContainer=function(){return!1===this.config.container?document.body:c.isElement(this.config.container)?e(this.config.container):e(document).find(this.config.container)},i._getAttachment=function(t){return K[t.toUpperCase()]},i._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(n){if("click"===n)e(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==n){var i="hover"===n?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,o="hover"===n?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;e(t.element).on(i,t.config.selector,(function(e){return t._enter(e)})).on(o,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},e(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=a(a({},this.config),{},{trigger:"manual",selector:""}):this._fixTitle()},i._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},i._enter=function(t,n){var i=this.constructor.DATA_KEY;(n=n||e(t.currentTarget).data(i))||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(i,n)),t&&(n._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),e(n.getTipElement()).hasClass("show")||"show"===n._hoverState?n._hoverState="show":(clearTimeout(n._timeout),n._hoverState="show",n.config.delay&&n.config.delay.show?n._timeout=setTimeout((function(){"show"===n._hoverState&&n.show()}),n.config.delay.show):n.show())},i._leave=function(t,n){var i=this.constructor.DATA_KEY;(n=n||e(t.currentTarget).data(i))||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(i,n)),t&&(n._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),n._isWithActiveTrigger()||(clearTimeout(n._timeout),n._hoverState="out",n.config.delay&&n.config.delay.hide?n._timeout=setTimeout((function(){"out"===n._hoverState&&n.hide()}),n.config.delay.hide):n.hide())},i._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},i._getConfig=function(t){var n=e(this.element).data();return Object.keys(n).forEach((function(t){-1!==V.indexOf(t)&&delete n[t]})),"number"==typeof(t=a(a(a({},this.constructor.Default),n),"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),c.typeCheckConfig(U,t,this.constructor.DefaultType),t.sanitize&&(t.template=H(t.template,t.whiteList,t.sanitizeFn)),t},i._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},i._cleanTipClass=function(){var t=e(this.getTipElement()),n=t.attr("class").match(W);null!==n&&n.length&&t.removeClass(n.join(""))},i._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},i._fixTransition=function(){var t=this.getTipElement(),n=this.config.animation;null===t.getAttribute("x-placement")&&(e(t).removeClass("fade"),this.config.animation=!1,this.hide(),this.show(),this.config.animation=n)},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.tooltip"),o="object"==typeof n&&n;if((i||!/dispose|hide/.test(n))&&(i||(i=new t(this,o),e(this).data("bs.tooltip",i)),"string"==typeof n)){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"Default",get:function(){return X}},{key:"NAME",get:function(){return U}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return Y}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return z}}]),t}();e.fn[U]=$._jQueryInterface,e.fn[U].Constructor=$,e.fn[U].noConflict=function(){return e.fn[U]=M,$._jQueryInterface};var J="popover",G=e.fn[J],Z=new RegExp("(^|\\s)bs-popover\\S+","g"),tt=a(a({},$.Default),{},{placement:"right",trigger:"click",content:"",template:''}),et=a(a({},$.DefaultType),{},{content:"(string|element|function)"}),nt={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},it=function(t){var n,i;function s(){return t.apply(this,arguments)||this}i=t,(n=s).prototype=Object.create(i.prototype),n.prototype.constructor=n,n.__proto__=i;var r=s.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){e(this.getTipElement()).addClass("bs-popover-"+t)},r.getTipElement=function(){return this.tip=this.tip||e(this.config.template)[0],this.tip},r.setContent=function(){var t=e(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var n=this._getContent();"function"==typeof n&&(n=n.call(this.element)),this.setElementContent(t.find(".popover-body"),n),t.removeClass("fade show")},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=e(this.getTipElement()),n=t.attr("class").match(Z);null!==n&&n.length>0&&t.removeClass(n.join(""))},s._jQueryInterface=function(t){return this.each((function(){var n=e(this).data("bs.popover"),i="object"==typeof t?t:null;if((n||!/dispose|hide/.test(t))&&(n||(n=new s(this,i),e(this).data("bs.popover",n)),"string"==typeof t)){if("undefined"==typeof n[t])throw new TypeError('No method named "'+t+'"');n[t]()}}))},o(s,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"Default",get:function(){return tt}},{key:"NAME",get:function(){return J}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return nt}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return et}}]),s}($);e.fn[J]=it._jQueryInterface,e.fn[J].Constructor=it,e.fn[J].noConflict=function(){return e.fn[J]=G,it._jQueryInterface};var ot="scrollspy",st=e.fn[ot],rt={offset:10,method:"auto",target:""},at={offset:"number",method:"string",target:"(string|element)"},lt=function(){function t(t,n){var i=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(n),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,e(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return i._process(t)})),this.refresh(),this._process()}var n=t.prototype;return n.refresh=function(){var t=this,n=this._scrollElement===this._scrollElement.window?"offset":"position",i="auto"===this._config.method?n:this._config.method,o="position"===i?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var n,s=c.getSelectorFromElement(t);if(s&&(n=document.querySelector(s)),n){var r=n.getBoundingClientRect();if(r.width||r.height)return[e(n)[i]().top+o,s]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},n.dispose=function(){e.removeData(this._element,"bs.scrollspy"),e(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},n._getConfig=function(t){if("string"!=typeof(t=a(a({},rt),"object"==typeof t&&t?t:{})).target&&c.isElement(t.target)){var n=e(t.target).attr("id");n||(n=c.getUID(ot),e(t.target).attr("id",n)),t.target="#"+n}return c.typeCheckConfig(ot,t,at),t},n._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},n._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},n._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},n._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t li > .active":".active";i=(i=e.makeArray(e(o).find(r)))[i.length-1]}var a=e.Event("hide.bs.tab",{relatedTarget:this._element}),l=e.Event("show.bs.tab",{relatedTarget:i});if(i&&e(i).trigger(a),e(this._element).trigger(l),!l.isDefaultPrevented()&&!a.isDefaultPrevented()){s&&(n=document.querySelector(s)),this._activate(this._element,o);var h=function(){var n=e.Event("hidden.bs.tab",{relatedTarget:t._element}),o=e.Event("shown.bs.tab",{relatedTarget:i});e(i).trigger(n),e(t._element).trigger(o)};n?this._activate(n,n.parentNode,h):h()}}},n.dispose=function(){e.removeData(this._element,"bs.tab"),this._element=null},n._activate=function(t,n,i){var o=this,s=(!n||"UL"!==n.nodeName&&"OL"!==n.nodeName?e(n).children(".active"):e(n).find("> li > .active"))[0],r=i&&s&&e(s).hasClass("fade"),a=function(){return o._transitionComplete(t,s,i)};if(s&&r){var l=c.getTransitionDurationFromElement(s);e(s).removeClass("show").one(c.TRANSITION_END,a).emulateTransitionEnd(l)}else a()},n._transitionComplete=function(t,n,i){if(n){e(n).removeClass("active");var o=e(n.parentNode).find("> .dropdown-menu .active")[0];o&&e(o).removeClass("active"),"tab"===n.getAttribute("role")&&n.setAttribute("aria-selected",!1)}if(e(t).addClass("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),c.reflow(t),t.classList.contains("fade")&&t.classList.add("show"),t.parentNode&&e(t.parentNode).hasClass("dropdown-menu")){var s=e(t).closest(".dropdown")[0];if(s){var r=[].slice.call(s.querySelectorAll(".dropdown-toggle"));e(r).addClass("active")}t.setAttribute("aria-expanded",!0)}i&&i()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.tab");if(o||(o=new t(this),i.data("bs.tab",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}}]),t}();e(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),ht._jQueryInterface.call(e(this),"show")})),e.fn.tab=ht._jQueryInterface,e.fn.tab.Constructor=ht,e.fn.tab.noConflict=function(){return e.fn.tab=ct,ht._jQueryInterface};var ut=e.fn.toast,dt={animation:"boolean",autohide:"boolean",delay:"number"},ft={animation:!0,autohide:!0,delay:500},gt=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var n=t.prototype;return n.show=function(){var t=this,n=e.Event("show.bs.toast");if(e(this._element).trigger(n),!n.isDefaultPrevented()){this._config.animation&&this._element.classList.add("fade");var i=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),e(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),c.reflow(this._element),this._element.classList.add("showing"),this._config.animation){var o=c.getTransitionDurationFromElement(this._element);e(this._element).one(c.TRANSITION_END,i).emulateTransitionEnd(o)}else i()}},n.hide=function(){if(this._element.classList.contains("show")){var t=e.Event("hide.bs.toast");e(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},n.dispose=function(){clearTimeout(this._timeout),this._timeout=null,this._element.classList.contains("show")&&this._element.classList.remove("show"),e(this._element).off("click.dismiss.bs.toast"),e.removeData(this._element,"bs.toast"),this._element=null,this._config=null},n._getConfig=function(t){return t=a(a(a({},ft),e(this._element).data()),"object"==typeof t&&t?t:{}),c.typeCheckConfig("toast",t,this.constructor.DefaultType),t},n._setListeners=function(){var t=this;e(this._element).on("click.dismiss.bs.toast",'[data-dismiss="toast"]',(function(){return t.hide()}))},n._close=function(){var t=this,n=function(){t._element.classList.add("hide"),e(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var i=c.getTransitionDurationFromElement(this._element);e(this._element).one(c.TRANSITION_END,n).emulateTransitionEnd(i)}else n()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.toast");if(o||(o=new t(this,"object"==typeof n&&n),i.data("bs.toast",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n](this)}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.0"}},{key:"DefaultType",get:function(){return dt}},{key:"Default",get:function(){return ft}}]),t}();e.fn.toast=gt._jQueryInterface,e.fn.toast.Constructor=gt,e.fn.toast.noConflict=function(){return e.fn.toast=ut,gt._jQueryInterface},t.Alert=d,t.Button=g,t.Carousel=E,t.Collapse=D,t.Dropdown=j,t.Modal=R,t.Popover=it,t.Scrollspy=lt,t.Tab=ht,t.Toast=gt,t.Tooltip=$,t.Util=c,Object.defineProperty(t,"__esModule",{value:!0})})); +//# sourceMappingURL=bootstrap.min.js.map \ No newline at end of file diff --git a/tests/coverage/_js/d3.min.js b/tests/coverage/_js/d3.min.js new file mode 100644 index 0000000..1664873 --- /dev/null +++ b/tests/coverage/_js/d3.min.js @@ -0,0 +1,5 @@ +!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++ie;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++aa;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)0?0:3:xo(r[0]-e)0?2:1:xo(r[1]-t)0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){ +r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)Uo?{x:s,y:xo(t-s)Uo?{x:xo(e-g)Uo?{x:h,y:xo(t-h)Uo?{x:xo(e-p)=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.yd||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.yr||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.yp){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.xu||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return ur;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++oe;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.ro;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++0;h--)o.push(u(c)*h);for(c=0;o[c]l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++oe?[NaN,NaN]:[e>0?a[e-1]:n[0],et?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ir&&(e=r)}else{for(;++i=r){e=r;break}for(;++ir&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ie&&(e=r)}else{for(;++i=r){e=r;break}for(;++ie&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}else{for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++ii){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++rr;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++uu;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv(" ","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], +shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++rn?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.xy&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++lt;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++cs?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u0)for(u=-1;++u=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.xg.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++it?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++oe&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0; +if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++ue.dx)&&(f=e.dx);++ue&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++au;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}(); \ No newline at end of file diff --git a/tests/coverage/_js/file.js b/tests/coverage/_js/file.js new file mode 100644 index 0000000..29cacd4 --- /dev/null +++ b/tests/coverage/_js/file.js @@ -0,0 +1,62 @@ + $(function() { + var $window = $(window) + , $top_link = $('#toplink') + , $body = $('body, html') + , offset = $('#code').offset().top + , hidePopover = function ($target) { + $target.data('popover-hover', false); + + setTimeout(function () { + if (!$target.data('popover-hover')) { + $target.popover('hide'); + } + }, 300); + }; + + $top_link.hide().click(function(event) { + event.preventDefault(); + $body.animate({scrollTop:0}, 800); + }); + + $window.scroll(function() { + if($window.scrollTop() > offset) { + $top_link.fadeIn(); + } else { + $top_link.fadeOut(); + } + }).scroll(); + + $('.popin') + .popover({trigger: 'manual'}) + .on({ + 'mouseenter.popover': function () { + var $target = $(this); + var $container = $target.children().first(); + + $target.data('popover-hover', true); + + // popover already displayed + if ($target.next('.popover').length) { + return; + } + + // show the popover + $container.popover('show'); + + // register mouse events on the popover + $target.next('.popover:not(.popover-initialized)') + .on({ + 'mouseenter': function () { + $target.data('popover-hover', true); + }, + 'mouseleave': function () { + hidePopover($container); + } + }) + .addClass('popover-initialized'); + }, + 'mouseleave.popover': function () { + hidePopover($(this).children().first()); + } + }); + }); diff --git a/tests/coverage/_js/jquery.min.js b/tests/coverage/_js/jquery.min.js new file mode 100644 index 0000000..b061403 --- /dev/null +++ b/tests/coverage/_js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0f&&(e=a.render.queue[f]);f++)d=e.generate(),typeof e.callback==typeof Function&&e.callback(d);a.render.queue.splice(0,f),a.render.queue.length?setTimeout(c):(a.dispatch.render_end(),a.render.active=!1)};setTimeout(c)},a.render.active=!1,a.render.queue=[],a.addGraph=function(b){typeof arguments[0]==typeof Function&&(b={generate:arguments[0],callback:arguments[1]}),a.render.queue.push(b),a.render.active||a.render()},"undefined"!=typeof module&&"undefined"!=typeof exports&&(module.exports=a),"undefined"!=typeof window&&(window.nv=a),a.dom.write=function(a){return void 0!==window.fastdom?fastdom.write(a):a()},a.dom.read=function(a){return void 0!==window.fastdom?fastdom.read(a):a()},a.interactiveGuideline=function(){"use strict";function b(l){l.each(function(l){function m(){var a=d3.mouse(this),d=a[0],e=a[1],i=!0,j=!1;if(k&&(d=d3.event.offsetX,e=d3.event.offsetY,"svg"!==d3.event.target.tagName&&(i=!1),d3.event.target.className.baseVal.match("nv-legend")&&(j=!0)),i&&(d-=f.left,e-=f.top),0>d||0>e||d>o||e>p||d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement||j){if(k&&d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement&&(void 0===d3.event.relatedTarget.className||d3.event.relatedTarget.className.match(c.nvPointerEventsClass)))return;return h.elementMouseout({mouseX:d,mouseY:e}),b.renderGuideLine(null),void c.hidden(!0)}c.hidden(!1);var l=g.invert(d);h.elementMousemove({mouseX:d,mouseY:e,pointXValue:l}),"dblclick"===d3.event.type&&h.elementDblclick({mouseX:d,mouseY:e,pointXValue:l}),"click"===d3.event.type&&h.elementClick({mouseX:d,mouseY:e,pointXValue:l})}var n=d3.select(this),o=d||960,p=e||400,q=n.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([l]),r=q.enter().append("g").attr("class"," nv-wrap nv-interactiveLineLayer");r.append("g").attr("class","nv-interactiveGuideLine"),j&&(j.on("touchmove",m).on("mousemove",m,!0).on("mouseout",m,!0).on("dblclick",m).on("click",m),b.guideLine=null,b.renderGuideLine=function(c){i&&(b.guideLine&&b.guideLine.attr("x1")===c||a.dom.write(function(){var b=q.select(".nv-interactiveGuideLine").selectAll("line").data(null!=c?[a.utils.NaNtoZero(c)]:[],String);b.enter().append("line").attr("class","nv-guideline").attr("x1",function(a){return a}).attr("x2",function(a){return a}).attr("y1",p).attr("y2",0),b.exit().remove()}))})})}var c=a.models.tooltip();c.duration(0).hideDelay(0)._isInteractiveLayer(!0).hidden(!1);var d=null,e=null,f={left:0,top:0},g=d3.scale.linear(),h=d3.dispatch("elementMousemove","elementMouseout","elementClick","elementDblclick"),i=!0,j=null,k="ActiveXObject"in window;return b.dispatch=h,b.tooltip=c,b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.xScale=function(a){return arguments.length?(g=a,b):g},b.showGuideLine=function(a){return arguments.length?(i=a,b):i},b.svgContainer=function(a){return arguments.length?(j=a,b):j},b},a.interactiveBisect=function(a,b,c){"use strict";if(!(a instanceof Array))return null;var d;d="function"!=typeof c?function(a){return a.x}:c;var e=function(a,b){return d(a)-b},f=d3.bisector(e).left,g=d3.max([0,f(a,b)-1]),h=d(a[g]);if("undefined"==typeof h&&(h=g),h===b)return g;var i=d3.min([g+1,a.length-1]),j=d(a[i]);return"undefined"==typeof j&&(j=i),Math.abs(j-b)>=Math.abs(h-b)?g:i},a.nearestValueIndex=function(a,b,c){"use strict";var d=1/0,e=null;return a.forEach(function(a,f){var g=Math.abs(b-a);null!=a&&d>=g&&c>g&&(d=g,e=f)}),e},function(){"use strict";a.models.tooltip=function(){function b(){if(k){var a=d3.select(k);"svg"!==a.node().tagName&&(a=a.select("svg"));var b=a.node()?a.attr("viewBox"):null;if(b){b=b.split(" ");var c=parseInt(a.style("width"),10)/b[2];p.left=p.left*c,p.top=p.top*c}}}function c(){if(!n){var a;a=k?k:document.body,n=d3.select(a).append("div").attr("class","nvtooltip "+(j?j:"xy-tooltip")).attr("id",v),n.style("top",0).style("left",0),n.style("opacity",0),n.selectAll("div, table, td, tr").classed(w,!0),n.classed(w,!0),o=n.node()}}function d(){if(r&&B(e)){b();var f=p.left,g=null!==i?i:p.top;return a.dom.write(function(){c();var b=A(e);b&&(o.innerHTML=b),k&&u?a.dom.read(function(){var a=k.getElementsByTagName("svg")[0],b={left:0,top:0};if(a){var c=a.getBoundingClientRect(),d=k.getBoundingClientRect(),e=c.top;if(0>e){var i=k.getBoundingClientRect();e=Math.abs(e)>i.height?0:e}b.top=Math.abs(e-d.top),b.left=Math.abs(c.left-d.left)}f+=k.offsetLeft+b.left-2*k.scrollLeft,g+=k.offsetTop+b.top-2*k.scrollTop,h&&h>0&&(g=Math.floor(g/h)*h),C([f,g])}):C([f,g])}),d}}var e=null,f="w",g=25,h=0,i=null,j=null,k=null,l=!0,m=400,n=null,o=null,p={left:null,top:null},q={left:0,top:0},r=!0,s=100,t=!0,u=!1,v="nvtooltip-"+Math.floor(1e5*Math.random()),w="nv-pointer-events-none",x=function(a){return a},y=function(a){return a},z=function(a){return a},A=function(a){if(null===a)return"";var b=d3.select(document.createElement("table"));if(t){var c=b.selectAll("thead").data([a]).enter().append("thead");c.append("tr").append("td").attr("colspan",3).append("strong").classed("x-value",!0).html(y(a.value))}var d=b.selectAll("tbody").data([a]).enter().append("tbody"),e=d.selectAll("tr").data(function(a){return a.series}).enter().append("tr").classed("highlight",function(a){return a.highlight});e.append("td").classed("legend-color-guide",!0).append("div").style("background-color",function(a){return a.color}),e.append("td").classed("key",!0).html(function(a,b){return z(a.key,b)}),e.append("td").classed("value",!0).html(function(a,b){return x(a.value,b)}),e.selectAll("td").each(function(a){if(a.highlight){var b=d3.scale.linear().domain([0,1]).range(["#fff",a.color]),c=.6;d3.select(this).style("border-bottom-color",b(c)).style("border-top-color",b(c))}});var f=b.node().outerHTML;return void 0!==a.footer&&(f+=""),f},B=function(a){if(a&&a.series){if(a.series instanceof Array)return!!a.series.length;if(a.series instanceof Object)return a.series=[a.series],!0}return!1},C=function(b){o&&a.dom.read(function(){var c,d,e=parseInt(o.offsetHeight,10),h=parseInt(o.offsetWidth,10),i=a.utils.windowSize().width,j=a.utils.windowSize().height,k=window.pageYOffset,p=window.pageXOffset;j=window.innerWidth>=document.body.scrollWidth?j:j-16,i=window.innerHeight>=document.body.scrollHeight?i:i-16;var r,t,u=function(a){var b=d;do isNaN(a.offsetTop)||(b+=a.offsetTop),a=a.offsetParent;while(a);return b},v=function(a){var b=c;do isNaN(a.offsetLeft)||(b+=a.offsetLeft),a=a.offsetParent;while(a);return b};switch(f){case"e":c=b[0]-h-g,d=b[1]-e/2,r=v(o),t=u(o),p>r&&(c=b[0]+g>p?b[0]+g:p-r+c),k>t&&(d=k-t+d),t+e>k+j&&(d=k+j-t+d-e);break;case"w":c=b[0]+g,d=b[1]-e/2,r=v(o),t=u(o),r+h>i&&(c=b[0]-h-g),k>t&&(d=k+5),t+e>k+j&&(d=k+j-t+d-e);break;case"n":c=b[0]-h/2-5,d=b[1]+g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),t+e>k+j&&(d=k+j-t+d-e);break;case"s":c=b[0]-h/2,d=b[1]-e-g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),k>t&&(d=k);break;case"none":c=b[0],d=b[1]-g,r=v(o),t=u(o)}c-=q.left,d-=q.top;var w=o.getBoundingClientRect(),k=window.pageYOffset||document.documentElement.scrollTop,p=window.pageXOffset||document.documentElement.scrollLeft,x="translate("+(w.left+p)+"px, "+(w.top+k)+"px)",y="translate("+c+"px, "+d+"px)",z=d3.interpolateString(x,y),A=n.style("opacity")<.1;l?n.transition().delay(m).duration(0).style("opacity",0):n.interrupt().transition().duration(A?0:s).styleTween("transform",function(){return z},"important").style("-webkit-transform",y).style("opacity",1)})};return d.nvPointerEventsClass=w,d.options=a.utils.optionsFunc.bind(d),d._options=Object.create({},{duration:{get:function(){return s},set:function(a){s=a}},gravity:{get:function(){return f},set:function(a){f=a}},distance:{get:function(){return g},set:function(a){g=a}},snapDistance:{get:function(){return h},set:function(a){h=a}},classes:{get:function(){return j},set:function(a){j=a}},chartContainer:{get:function(){return k},set:function(a){k=a}},fixedTop:{get:function(){return i},set:function(a){i=a}},enabled:{get:function(){return r},set:function(a){r=a}},hideDelay:{get:function(){return m},set:function(a){m=a}},contentGenerator:{get:function(){return A},set:function(a){A=a}},valueFormatter:{get:function(){return x},set:function(a){x=a}},headerFormatter:{get:function(){return y},set:function(a){y=a}},keyFormatter:{get:function(){return z},set:function(a){z=a}},headerEnabled:{get:function(){return t},set:function(a){t=a}},_isInteractiveLayer:{get:function(){return u},set:function(a){u=!!a}},position:{get:function(){return p},set:function(a){p.left=void 0!==a.left?a.left:p.left,p.top=void 0!==a.top?a.top:p.top}},offset:{get:function(){return q},set:function(a){q.left=void 0!==a.left?a.left:q.left,q.top=void 0!==a.top?a.top:q.top}},hidden:{get:function(){return l},set:function(a){l!=a&&(l=!!a,d())}},data:{get:function(){return e},set:function(a){a.point&&(a.value=a.point.x,a.series=a.series||{},a.series.value=a.point.y,a.series.color=a.point.color||a.series.color),e=a}},tooltipElem:{get:function(){return o},set:function(){}},id:{get:function(){return v},set:function(){}}}),a.utils.initOptions(d),d}}(),a.utils.windowSize=function(){var a={width:640,height:480};return window.innerWidth&&window.innerHeight?(a.width=window.innerWidth,a.height=window.innerHeight,a):"CSS1Compat"==document.compatMode&&document.documentElement&&document.documentElement.offsetWidth?(a.width=document.documentElement.offsetWidth,a.height=document.documentElement.offsetHeight,a):document.body&&document.body.offsetWidth?(a.width=document.body.offsetWidth,a.height=document.body.offsetHeight,a):a},a.utils.windowResize=function(b){return window.addEventListener?window.addEventListener("resize",b):a.log("ERROR: Failed to bind to window.resize with: ",b),{callback:b,clear:function(){window.removeEventListener("resize",b)}}},a.utils.getColor=function(b){if(void 0===b)return a.utils.defaultColor();if(Array.isArray(b)){var c=d3.scale.ordinal().range(b);return function(a,b){var d=void 0===b?a:b;return a.color||c(d)}}return b},a.utils.defaultColor=function(){return a.utils.getColor(d3.scale.category20().range())},a.utils.customTheme=function(a,b,c){b=b||function(a){return a.key},c=c||d3.scale.category20().range();var d=c.length;return function(e){var f=b(e);return"function"==typeof a[f]?a[f]():void 0!==a[f]?a[f]:(d||(d=c.length),d-=1,c[d])}},a.utils.pjax=function(b,c){var d=function(d){d3.html(d,function(d){var e=d3.select(c).node();e.parentNode.replaceChild(d3.select(d).select(c).node(),e),a.utils.pjax(b,c)})};d3.selectAll(b).on("click",function(){history.pushState(this.href,this.textContent,this.href),d(this.href),d3.event.preventDefault()}),d3.select(window).on("popstate",function(){d3.event.state&&d(d3.event.state)})},a.utils.calcApproxTextWidth=function(a){if("function"==typeof a.style&&"function"==typeof a.text){var b=parseInt(a.style("font-size").replace("px",""),10),c=a.text().length;return c*b*.5}return 0},a.utils.NaNtoZero=function(a){return"number"!=typeof a||isNaN(a)||null===a||1/0===a||a===-1/0?0:a},d3.selection.prototype.watchTransition=function(a){var b=[this].concat([].slice.call(arguments,1));return a.transition.apply(a,b)},a.utils.renderWatch=function(b,c){if(!(this instanceof a.utils.renderWatch))return new a.utils.renderWatch(b,c);var d=void 0!==c?c:250,e=[],f=this;this.models=function(a){return a=[].slice.call(arguments,0),a.forEach(function(a){a.__rendered=!1,function(a){a.dispatch.on("renderEnd",function(){a.__rendered=!0,f.renderEnd("model")})}(a),e.indexOf(a)<0&&e.push(a)}),this},this.reset=function(a){void 0!==a&&(d=a),e=[]},this.transition=function(a,b,c){if(b=arguments.length>1?[].slice.call(arguments,1):[],c=b.length>1?b.pop():void 0!==d?d:250,a.__rendered=!1,e.indexOf(a)<0&&e.push(a),0===c)return a.__rendered=!0,a.delay=function(){return this},a.duration=function(){return this},a;a.__rendered=0===a.length?!0:a.every(function(a){return!a.length})?!0:!1;var g=0;return a.transition().duration(c).each(function(){++g}).each("end",function(){0===--g&&(a.__rendered=!0,f.renderEnd.apply(this,b))})},this.renderEnd=function(){e.every(function(a){return a.__rendered})&&(e.forEach(function(a){a.__rendered=!1}),b.renderEnd.apply(this,arguments))}},a.utils.deepExtend=function(b){var c=arguments.length>1?[].slice.call(arguments,1):[];c.forEach(function(c){for(var d in c){var e=b[d]instanceof Array,f="object"==typeof b[d],g="object"==typeof c[d];f&&!e&&g?a.utils.deepExtend(b[d],c[d]):b[d]=c[d]}})},a.utils.state=function(){if(!(this instanceof a.utils.state))return new a.utils.state;var b={},c=function(){},d=function(){return{}},e=null,f=null;this.dispatch=d3.dispatch("change","set"),this.dispatch.on("set",function(a){c(a,!0)}),this.getter=function(a){return d=a,this},this.setter=function(a,b){return b||(b=function(){}),c=function(c,d){a(c),d&&b()},this},this.init=function(b){e=e||{},a.utils.deepExtend(e,b)};var g=function(){var a=d();if(JSON.stringify(a)===JSON.stringify(b))return!1;for(var c in a)void 0===b[c]&&(b[c]={}),b[c]=a[c],f=!0;return!0};this.update=function(){e&&(c(e,!1),e=null),g.call(this)&&this.dispatch.change(b)}},a.utils.optionsFunc=function(a){return a&&d3.map(a).forEach(function(a,b){"function"==typeof this[a]&&this[a](b)}.bind(this)),this},a.utils.calcTicksX=function(b,c){var d=1,e=0;for(e;ed?f:d}return a.log("Requested number of ticks: ",b),a.log("Calculated max values to be: ",d),b=b>d?b=d-1:b,b=1>b?1:b,b=Math.floor(b),a.log("Calculating tick count as: ",b),b},a.utils.calcTicksY=function(b,c){return a.utils.calcTicksX(b,c)},a.utils.initOption=function(a,b){a._calls&&a._calls[b]?a[b]=a._calls[b]:(a[b]=function(c){return arguments.length?(a._overrides[b]=!0,a._options[b]=c,a):a._options[b]},a["_"+b]=function(c){return arguments.length?(a._overrides[b]||(a._options[b]=c),a):a._options[b]})},a.utils.initOptions=function(b){b._overrides=b._overrides||{};var c=Object.getOwnPropertyNames(b._options||{}),d=Object.getOwnPropertyNames(b._calls||{});c=c.concat(d);for(var e in c)a.utils.initOption(b,c[e])},a.utils.inheritOptionsD3=function(a,b,c){a._d3options=c.concat(a._d3options||[]),c.unshift(b),c.unshift(a),d3.rebind.apply(this,c)},a.utils.arrayUnique=function(a){return a.sort().filter(function(b,c){return!c||b!=a[c-1]})},a.utils.symbolMap=d3.map(),a.utils.symbol=function(){function b(b,e){var f=c.call(this,b,e),g=d.call(this,b,e);return-1!==d3.svg.symbolTypes.indexOf(f)?d3.svg.symbol().type(f).size(g)():a.utils.symbolMap.get(f)(g)}var c,d=64;return b.type=function(a){return arguments.length?(c=d3.functor(a),b):c},b.size=function(a){return arguments.length?(d=d3.functor(a),b):d},b},a.utils.inheritOptions=function(b,c){var d=Object.getOwnPropertyNames(c._options||{}),e=Object.getOwnPropertyNames(c._calls||{}),f=c._inherited||[],g=c._d3options||[],h=d.concat(e).concat(f).concat(g);h.unshift(c),h.unshift(b),d3.rebind.apply(this,h),b._inherited=a.utils.arrayUnique(d.concat(e).concat(f).concat(d).concat(b._inherited||[])),b._d3options=a.utils.arrayUnique(g.concat(b._d3options||[]))},a.utils.initSVG=function(a){a.classed({"nvd3-svg":!0})},a.utils.sanitizeHeight=function(a,b){return a||parseInt(b.style("height"),10)||400},a.utils.sanitizeWidth=function(a,b){return a||parseInt(b.style("width"),10)||960},a.utils.availableHeight=function(b,c,d){return a.utils.sanitizeHeight(b,c)-d.top-d.bottom},a.utils.availableWidth=function(b,c,d){return a.utils.sanitizeWidth(b,c)-d.left-d.right},a.utils.noData=function(b,c){var d=b.options(),e=d.margin(),f=d.noData(),g=null==f?["No Data Available."]:[f],h=a.utils.availableHeight(d.height(),c,e),i=a.utils.availableWidth(d.width(),c,e),j=e.left+i/2,k=e.top+h/2;c.selectAll("g").remove();var l=c.selectAll(".nv-noData").data(g);l.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),l.attr("x",j).attr("y",k).text(function(a){return a})},a.models.axis=function(){"use strict";function b(g){return s.reset(),g.each(function(b){var g=d3.select(this);a.utils.initSVG(g);var p=g.selectAll("g.nv-wrap.nv-axis").data([b]),q=p.enter().append("g").attr("class","nvd3 nv-wrap nv-axis"),t=(q.append("g"),p.select("g"));null!==n?c.ticks(n):("top"==c.orient()||"bottom"==c.orient())&&c.ticks(Math.abs(d.range()[1]-d.range()[0])/100),t.watchTransition(s,"axis").call(c),r=r||c.scale();var u=c.tickFormat();null==u&&(u=r.tickFormat());var v=t.selectAll("text.nv-axislabel").data([h||null]);v.exit().remove();var w,x,y;switch(c.orient()){case"top":v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",0).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b))+",0)"}).select("text").attr("dy","-0.5em").attr("y",-c.tickPadding()).attr("text-anchor","middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max top").attr("transform",function(b,c){return"translate("+a.utils.NaNtoZero(d.range()[c])+",0)"}));break;case"bottom":w=o+36;var z=30,A=0,B=t.selectAll("g").select("text"),C="";if(j%360){B.each(function(){var a=this.getBoundingClientRect(),b=a.width;A=a.height,b>z&&(z=b)}),C="rotate("+j+" 0,"+(A/2+c.tickPadding())+")";var D=Math.abs(Math.sin(j*Math.PI/180));w=(D?D*z:z)+30,B.attr("transform",C).style("text-anchor",j%360>0?"start":"end")}v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",w).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data([d.domain()[0],d.domain()[d.domain().length-1]]),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",c.tickPadding()).attr("transform",C).style("text-anchor",j?j%360>0?"start":"end":"middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max bottom").attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"})),l&&B.attr("transform",function(a,b){return"translate(0,"+(b%2==0?"0":"12")+")"});break;case"right":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"begin").attr("transform",k?"rotate(90)":"").attr("y",k?-Math.max(e.right,f)+12:-10).attr("x",k?d3.max(d.range())/2:c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(d(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",c.tickPadding()).style("text-anchor","start").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1));break;case"left":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"end").attr("transform",k?"rotate(-90)":"").attr("y",k?-Math.max(e.left,f)+25-(o||0):-10).attr("x",k?-d3.max(d.range())/2:-c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(r(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-c.tickPadding()).attr("text-anchor","end").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1))}if(v.text(function(a){return a}),!i||"left"!==c.orient()&&"right"!==c.orient()||(t.selectAll("g").each(function(a){d3.select(this).select("text").attr("opacity",1),(d(a)d.range()[0]-10)&&((a>1e-10||-1e-10>a)&&d3.select(this).attr("opacity",0),d3.select(this).select("text").attr("opacity",0))}),d.domain()[0]==d.domain()[1]&&0==d.domain()[0]&&p.selectAll("g.nv-axisMaxMin").style("opacity",function(a,b){return b?0:1})),i&&("top"===c.orient()||"bottom"===c.orient())){var E=[];p.selectAll("g.nv-axisMaxMin").each(function(a,b){try{E.push(b?d(a)-this.getBoundingClientRect().width-4:d(a)+this.getBoundingClientRect().width+4)}catch(c){E.push(b?d(a)-4:d(a)+4)}}),t.selectAll("g").each(function(a){(d(a)E[1])&&(a>1e-10||-1e-10>a?d3.select(this).remove():d3.select(this).select("text").remove())})}t.selectAll(".tick").filter(function(a){return!parseFloat(Math.round(1e5*a)/1e6)&&void 0!==a}).classed("zero",!0),r=d.copy()}),s.renderEnd("axis immediate"),b}var c=d3.svg.axis(),d=d3.scale.linear(),e={top:0,right:0,bottom:0,left:0},f=75,g=60,h=null,i=!0,j=0,k=!0,l=!1,m=!1,n=null,o=0,p=250,q=d3.dispatch("renderEnd");c.scale(d).orient("bottom").tickFormat(function(a){return a});var r,s=a.utils.renderWatch(q,p);return b.axis=c,b.dispatch=q,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{axisLabelDistance:{get:function(){return o},set:function(a){o=a}},staggerLabels:{get:function(){return l},set:function(a){l=a}},rotateLabels:{get:function(){return j},set:function(a){j=a}},rotateYLabel:{get:function(){return k},set:function(a){k=a}},showMaxMin:{get:function(){return i},set:function(a){i=a}},axisLabel:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return g},set:function(a){g=a}},ticks:{get:function(){return n},set:function(a){n=a}},width:{get:function(){return f},set:function(a){f=a}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},duration:{get:function(){return p},set:function(a){p=a,s.reset(p)}},scale:{get:function(){return d},set:function(e){d=e,c.scale(d),m="function"==typeof d.rangeBands,a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"])}}}),a.utils.initOptions(b),a.utils.inheritOptionsD3(b,c,["orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat"]),a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"]),b},a.models.boxPlot=function(){"use strict";function b(l){return v.reset(),l.each(function(b){var l=j-i.left-i.right,p=k-i.top-i.bottom;r=d3.select(this),a.utils.initSVG(r),m.domain(c||b.map(function(a,b){return o(a,b)})).rangeBands(e||[0,l],.1);var w=[];if(!d){var x=d3.min(b.map(function(a){var b=[];return b.push(a.values.Q1),a.values.hasOwnProperty("whisker_low")&&null!==a.values.whisker_low&&b.push(a.values.whisker_low),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.min(b)})),y=d3.max(b.map(function(a){var b=[];return b.push(a.values.Q3),a.values.hasOwnProperty("whisker_high")&&null!==a.values.whisker_high&&b.push(a.values.whisker_high),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.max(b)}));w=[x,y]}n.domain(d||w),n.range(f||[p,0]),g=g||m,h=h||n.copy().range([n(0),n(0)]);{var z=r.selectAll("g.nv-wrap").data([b]);z.enter().append("g").attr("class","nvd3 nv-wrap")}z.attr("transform","translate("+i.left+","+i.top+")");var A=z.selectAll(".nv-boxplot").data(function(a){return a}),B=A.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);A.attr("class","nv-boxplot").attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}).classed("hover",function(a){return a.hover}),A.watchTransition(v,"nv-boxplot: boxplots").style("stroke-opacity",1).style("fill-opacity",.75).delay(function(a,c){return c*t/b.length}).attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}),A.exit().remove(),B.each(function(a,b){var c=d3.select(this);["low","high"].forEach(function(d){a.values.hasOwnProperty("whisker_"+d)&&null!==a.values["whisker_"+d]&&(c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-whisker nv-boxplot-"+d),c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-tick nv-boxplot-"+d))})});var C=A.selectAll(".nv-boxplot-outlier").data(function(a){return a.values.hasOwnProperty("outliers")&&null!==a.values.outliers?a.values.outliers:[]});C.enter().append("circle").style("fill",function(a,b,c){return q(a,c)}).style("stroke",function(a,b,c){return q(a,c)}).on("mouseover",function(a,b,c){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:a,color:q(a,c)},e:d3.event})}).on("mouseout",function(a,b,c){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:a,color:q(a,c)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),C.attr("class","nv-boxplot-outlier"),C.watchTransition(v,"nv-boxplot: nv-boxplot-outlier").attr("cx",.45*m.rangeBand()).attr("cy",function(a){return n(a)}).attr("r","3"),C.exit().remove();var D=function(){return null===u?.9*m.rangeBand():Math.min(75,.9*m.rangeBand())},E=function(){return.45*m.rangeBand()-D()/2},F=function(){return.45*m.rangeBand()+D()/2};["low","high"].forEach(function(a){var b="low"===a?"Q1":"Q3";A.select("line.nv-boxplot-whisker.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",.45*m.rangeBand()).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",.45*m.rangeBand()).attr("y2",function(a){return n(a.values[b])}),A.select("line.nv-boxplot-tick.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",E).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",F).attr("y2",function(b){return n(b.values["whisker_"+a])})}),["low","high"].forEach(function(a){B.selectAll(".nv-boxplot-"+a).on("mouseover",function(b,c,d){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mouseout",function(b,c,d){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})})}),B.append("rect").attr("class","nv-boxplot-box").on("mouseover",function(a,b){d3.select(this).classed("hover",!0),s.elementMouseover({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),s.elementMouseout({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),A.select("rect.nv-boxplot-box").watchTransition(v,"nv-boxplot: boxes").attr("y",function(a){return n(a.values.Q3)}).attr("width",D).attr("x",E).attr("height",function(a){return Math.abs(n(a.values.Q3)-n(a.values.Q1))||1}).style("fill",function(a,b){return a.color||q(a,b)}).style("stroke",function(a,b){return a.color||q(a,b)}),B.append("line").attr("class","nv-boxplot-median"),A.select("line.nv-boxplot-median").watchTransition(v,"nv-boxplot: boxplots line").attr("x1",E).attr("y1",function(a){return n(a.values.Q2)}).attr("x2",F).attr("y2",function(a){return n(a.values.Q2)}),g=m.copy(),h=n.copy()}),v.renderEnd("nv-boxplot immediate"),b}var c,d,e,f,g,h,i={top:0,right:0,bottom:0,left:0},j=960,k=500,l=Math.floor(1e4*Math.random()),m=d3.scale.ordinal(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=a.utils.defaultColor(),r=null,s=d3.dispatch("elementMouseover","elementMouseout","elementMousemove","renderEnd"),t=250,u=null,v=a.utils.renderWatch(s,t);return b.dispatch=s,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},maxBoxWidth:{get:function(){return u},set:function(a){u=a}},x:{get:function(){return o},set:function(a){o=a}},y:{get:function(){return p},set:function(a){p=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return l},set:function(a){l=a}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}},duration:{get:function(){return t},set:function(a){t=a,v.reset(t)}}}),a.utils.initOptions(b),b},a.models.boxPlotChart=function(){"use strict";function b(k){return t.reset(),t.models(e),l&&t.models(f),m&&t.models(g),k.each(function(k){var p=d3.select(this);a.utils.initSVG(p);var t=(i||parseInt(p.style("width"))||960)-h.left-h.right,u=(j||parseInt(p.style("height"))||400)-h.top-h.bottom;if(b.update=function(){r.beforeUpdate(),p.transition().duration(s).call(b)},b.container=this,!(k&&k.length&&k.filter(function(a){return a.values.hasOwnProperty("Q1")&&a.values.hasOwnProperty("Q2")&&a.values.hasOwnProperty("Q3")}).length)){var v=p.selectAll(".nv-noData").data([q]);return v.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),v.attr("x",h.left+t/2).attr("y",h.top+u/2).text(function(a){return a}),b}p.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var w=p.selectAll("g.nv-wrap.nv-boxPlotWithAxes").data([k]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-boxPlotWithAxes").append("g"),y=x.append("defs"),z=w.select("g"); +x.append("g").attr("class","nv-x nv-axis"),x.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),x.append("g").attr("class","nv-barsWrap"),z.attr("transform","translate("+h.left+","+h.top+")"),n&&z.select(".nv-y.nv-axis").attr("transform","translate("+t+",0)"),e.width(t).height(u);var A=z.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));if(A.transition().call(e),y.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),z.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(o?2:1)).attr("height",16).attr("x",-c.rangeBand()/(o?1:2)),l){f.scale(c).ticks(a.utils.calcTicksX(t/100,k)).tickSize(-u,0),z.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),z.select(".nv-x.nv-axis").call(f);var B=z.select(".nv-x.nv-axis").selectAll("g");o&&B.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}m&&(g.scale(d).ticks(Math.floor(u/36)).tickSize(-t,0),z.select(".nv-y.nv-axis").call(g)),z.select(".nv-zeroLine line").attr("x1",0).attr("x2",t).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("nv-boxplot chart immediate"),b}var c,d,e=a.models.boxPlot(),f=a.models.axis(),g=a.models.axis(),h={top:15,right:10,bottom:50,left:60},i=null,j=null,k=a.utils.getColor(),l=!0,m=!0,n=!1,o=!1,p=a.models.tooltip(),q="No Data Available.",r=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(n?"right":"left").tickFormat(d3.format(",.1f")),p.duration(0);var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){p.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(a){p.data(a).hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){p.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.boxplot=e,b.xAxis=f,b.yAxis=g,b.tooltip=p,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},staggerLabels:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return l},set:function(a){l=a}},showYAxis:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return tooltips},set:function(a){tooltips=a}},tooltipContent:{get:function(){return p},set:function(a){p=a}},noData:{get:function(){return q},set:function(a){q=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!==a.top?a.top:h.top,h.right=void 0!==a.right?a.right:h.right,h.bottom=void 0!==a.bottom?a.bottom:h.bottom,h.left=void 0!==a.left?a.left:h.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}},rightAlignYAxis:{get:function(){return n},set:function(a){n=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.bullet=function(){"use strict";function b(d){return d.each(function(b,d){var p=m-c.left-c.right,s=n-c.top-c.bottom;o=d3.select(this),a.utils.initSVG(o);{var t=f.call(this,b,d).slice().sort(d3.descending),u=g.call(this,b,d).slice().sort(d3.descending),v=h.call(this,b,d).slice().sort(d3.descending),w=i.call(this,b,d).slice(),x=j.call(this,b,d).slice(),y=k.call(this,b,d).slice(),z=d3.scale.linear().domain(d3.extent(d3.merge([l,t]))).range(e?[p,0]:[0,p]);this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range())}this.__chart__=z;var A=d3.min(t),B=d3.max(t),C=t[1],D=o.selectAll("g.nv-wrap.nv-bullet").data([b]),E=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet"),F=E.append("g"),G=D.select("g");F.append("rect").attr("class","nv-range nv-rangeMax"),F.append("rect").attr("class","nv-range nv-rangeAvg"),F.append("rect").attr("class","nv-range nv-rangeMin"),F.append("rect").attr("class","nv-measure"),D.attr("transform","translate("+c.left+","+c.top+")");var H=function(a){return Math.abs(z(a)-z(0))},I=function(a){return z(0>a?a:0)};G.select("rect.nv-rangeMax").attr("height",s).attr("width",H(B>0?B:A)).attr("x",I(B>0?B:A)).datum(B>0?B:A),G.select("rect.nv-rangeAvg").attr("height",s).attr("width",H(C)).attr("x",I(C)).datum(C),G.select("rect.nv-rangeMin").attr("height",s).attr("width",H(B)).attr("x",I(B)).attr("width",H(B>0?A:B)).attr("x",I(B>0?A:B)).datum(B>0?A:B),G.select("rect.nv-measure").style("fill",q).attr("height",s/3).attr("y",s/3).attr("width",0>v?z(0)-z(v[0]):z(v[0])-z(0)).attr("x",I(v)).on("mouseover",function(){r.elementMouseover({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mouseout",function(){r.elementMouseout({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})});var J=s/6,K=u.map(function(a,b){return{value:a,label:x[b]}});F.selectAll("path.nv-markerTriangle").data(K).enter().append("path").attr("class","nv-markerTriangle").attr("transform",function(a){return"translate("+z(a.value)+","+s/2+")"}).attr("d","M0,"+J+"L"+J+","+-J+" "+-J+","+-J+"Z").on("mouseover",function(a){r.elementMouseover({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill"),pos:[z(a.value),s/2]})}).on("mousemove",function(a){r.elementMousemove({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a){r.elementMouseout({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}),D.selectAll(".nv-range").on("mouseover",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseover({value:a,label:c,color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseout({value:a,label:c,color:d3.select(this).style("fill")})})}),b}var c={top:0,right:0,bottom:0,left:0},d="left",e=!1,f=function(a){return a.ranges},g=function(a){return a.markers?a.markers:[0]},h=function(a){return a.measures},i=function(a){return a.rangeLabels?a.rangeLabels:[]},j=function(a){return a.markerLabels?a.markerLabels:[]},k=function(a){return a.measureLabels?a.measureLabels:[]},l=[0],m=380,n=30,o=null,p=null,q=a.utils.getColor(["#1f77b4"]),r=d3.dispatch("elementMouseover","elementMouseout","elementMousemove");return b.dispatch=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return f},set:function(a){f=a}},markers:{get:function(){return g},set:function(a){g=a}},measures:{get:function(){return h},set:function(a){h=a}},forceX:{get:function(){return l},set:function(a){l=a}},width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},tickFormat:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},orient:{get:function(){return d},set:function(a){d=a,e="right"==d||"bottom"==d}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.bulletChart=function(){"use strict";function b(d){return d.each(function(e,o){var p=d3.select(this);a.utils.initSVG(p);var q=a.utils.availableWidth(k,p,g),r=l-g.top-g.bottom;if(b.update=function(){b(d)},b.container=this,!e||!h.call(this,e,o))return a.utils.noData(b,p),b;p.selectAll(".nv-noData").remove();var s=h.call(this,e,o).slice().sort(d3.descending),t=i.call(this,e,o).slice().sort(d3.descending),u=j.call(this,e,o).slice().sort(d3.descending),v=p.selectAll("g.nv-wrap.nv-bulletChart").data([e]),w=v.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart"),x=w.append("g"),y=v.select("g");x.append("g").attr("class","nv-bulletWrap"),x.append("g").attr("class","nv-titles"),v.attr("transform","translate("+g.left+","+g.top+")");var z=d3.scale.linear().domain([0,Math.max(s[0],t[0],u[0])]).range(f?[q,0]:[0,q]),A=this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range());this.__chart__=z;var B=x.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(l-g.top-g.bottom)/2+")");B.append("text").attr("class","nv-title").text(function(a){return a.title}),B.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(a){return a.subtitle}),c.width(q).height(r);var C=y.select(".nv-bulletWrap");d3.transition(C).call(c);var D=m||z.tickFormat(q/100),E=y.selectAll("g.nv-tick").data(z.ticks(n?n:q/50),function(a){return this.textContent||D(a)}),F=E.enter().append("g").attr("class","nv-tick").attr("transform",function(a){return"translate("+A(a)+",0)"}).style("opacity",1e-6);F.append("line").attr("y1",r).attr("y2",7*r/6),F.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",7*r/6).text(D);var G=d3.transition(E).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1);G.select("line").attr("y1",r).attr("y2",7*r/6),G.select("text").attr("y",7*r/6),d3.transition(E.exit()).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1e-6).remove()}),d3.timer.flush(),b}var c=a.models.bullet(),d=a.models.tooltip(),e="left",f=!1,g={top:5,right:40,bottom:20,left:120},h=function(a){return a.ranges},i=function(a){return a.markers?a.markers:[0]},j=function(a){return a.measures},k=null,l=55,m=null,n=null,o=null,p=d3.dispatch("tooltipShow","tooltipHide");return d.duration(0).headerEnabled(!1),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.label,value:a.value,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.bullet=c,b.dispatch=p,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return h},set:function(a){h=a}},markers:{get:function(){return i},set:function(a){i=a}},measures:{get:function(){return j},set:function(a){j=a}},width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},tickFormat:{get:function(){return m},set:function(a){m=a}},ticks:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return o},set:function(a){o=a}},tooltips:{get:function(){return d.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),d.enabled(!!b)}},tooltipContent:{get:function(){return d.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),d.contentGenerator(b)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},orient:{get:function(){return e},set:function(a){e=a,f="right"==e||"bottom"==e}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.candlestickBar=function(){"use strict";function b(x){return x.each(function(b){c=d3.select(this);var x=a.utils.availableWidth(i,c,h),y=a.utils.availableHeight(j,c,h);a.utils.initSVG(c);var A=x/b[0].values.length*.45;l.domain(d||d3.extent(b[0].values.map(n).concat(t))),l.range(v?f||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:f||[5+A/2,x-A/2-5]),m.domain(e||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(g||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var B=d3.select(this).selectAll("g.nv-wrap.nv-candlestickBar").data([b[0].values]),C=B.enter().append("g").attr("class","nvd3 nv-wrap nv-candlestickBar"),D=C.append("defs"),E=C.append("g"),F=B.select("g");E.append("g").attr("class","nv-ticks"),B.attr("transform","translate("+h.left+","+h.top+")"),c.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:k})}),D.append("clipPath").attr("id","nv-chart-clip-path-"+k).append("rect"),B.select("#nv-chart-clip-path-"+k+" rect").attr("width",x).attr("height",y),F.attr("clip-path",w?"url(#nv-chart-clip-path-"+k+")":"");var G=B.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});G.exit().remove();{var H=G.enter().append("g").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b});H.append("line").attr("class","nv-candlestick-lines").attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),H.append("rect").attr("class","nv-candlestick-rects nv-bars").attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}c.selectAll(".nv-candlestick-lines").transition().attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),c.selectAll(".nv-candlestick-rects").transition().attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}),b}var c,d,e,f,g,h={top:0,right:0,bottom:0,left:0},i=null,j=null,k=Math.floor(1e4*Math.random()),l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,d){b.clearHighlights(),c.select(".nv-candlestickBar .nv-tick-0-"+a).classed("hover",d)},b.clearHighlights=function(){c.select(".nv-candlestickBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return k},set:function(a){k=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!=a.top?a.top:h.top,h.right=void 0!=a.right?a.right:h.right,h.bottom=void 0!=a.bottom?a.bottom:h.bottom,h.left=void 0!=a.left?a.left:h.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.cumulativeLineChart=function(){"use strict";function b(l){return H.reset(),H.models(f),r&&H.models(g),s&&H.models(h),l.each(function(l){function A(){d3.select(b.container).style("cursor","ew-resize")}function E(){G.x=d3.event.x,G.i=Math.round(F.invert(G.x)),K()}function H(){d3.select(b.container).style("cursor","auto"),y.index=G.i,C.stateChange(y)}function K(){bb.data([G]);var a=b.duration();b.duration(0),b.update(),b.duration(a)}var L=d3.select(this);a.utils.initSVG(L),L.classed("nv-chart-"+x,!0);var M=this,N=a.utils.availableWidth(o,L,m),O=a.utils.availableHeight(p,L,m);if(b.update=function(){0===D?L.call(b):L.transition().duration(D).call(b)},b.container=this,y.setter(J(l),b.update).getter(I(l)).update(),y.disabled=l.map(function(a){return!!a.disabled}),!z){var P;z={};for(P in y)z[P]=y[P]instanceof Array?y[P].slice(0):y[P]}var Q=d3.behavior.drag().on("dragstart",A).on("drag",E).on("dragend",H);if(!(l&&l.length&&l.filter(function(a){return a.values.length}).length))return a.utils.noData(b,L),b;if(L.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale(),w)f.yDomain(null);else{var R=l.filter(function(a){return!a.disabled}).map(function(a){var b=d3.extent(a.values,f.y());return b[0]<-.95&&(b[0]=-.95),[(b[0]-b[1])/(1+b[1]),(b[1]-b[0])/(1+b[0])]}),S=[d3.min(R,function(a){return a[0]}),d3.max(R,function(a){return a[1]})];f.yDomain(S)}F.domain([0,l[0].values.length-1]).range([0,N]).clamp(!0);var l=c(G.i,l),T=v?"none":"all",U=L.selectAll("g.nv-wrap.nv-cumulativeLine").data([l]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g"),W=U.select("g");if(V.append("g").attr("class","nv-interactive"),V.append("g").attr("class","nv-x nv-axis").style("pointer-events","none"),V.append("g").attr("class","nv-y nv-axis"),V.append("g").attr("class","nv-background"),V.append("g").attr("class","nv-linesWrap").style("pointer-events",T),V.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none"),V.append("g").attr("class","nv-legendWrap"),V.append("g").attr("class","nv-controlsWrap"),q&&(i.width(N),W.select(".nv-legendWrap").datum(l).call(i),m.top!=i.height()&&(m.top=i.height(),O=a.utils.availableHeight(p,L,m)),W.select(".nv-legendWrap").attr("transform","translate(0,"+-m.top+")")),u){var X=[{key:"Re-scale y-axis",disabled:!w}];j.width(140).color(["#444","#444","#444"]).rightAlign(!1).margin({top:5,right:0,bottom:5,left:20}),W.select(".nv-controlsWrap").datum(X).attr("transform","translate(0,"+-m.top+")").call(j)}U.attr("transform","translate("+m.left+","+m.top+")"),t&&W.select(".nv-y.nv-axis").attr("transform","translate("+N+",0)");var Y=l.filter(function(a){return a.tempDisabled});U.select(".tempDisabled").remove(),Y.length&&U.append("text").attr("class","tempDisabled").attr("x",N/2).attr("y","-.71em").style("text-anchor","end").text(Y.map(function(a){return a.key}).join(", ")+" values cannot be calculated for this time period."),v&&(k.width(N).height(O).margin({left:m.left,top:m.top}).svgContainer(L).xScale(d),U.select(".nv-interactive").call(k)),V.select(".nv-background").append("rect"),W.select(".nv-background rect").attr("width",N).attr("height",O),f.y(function(a){return a.display.y}).width(N).height(O).color(l.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!l[b].disabled&&!l[b].tempDisabled}));var Z=W.select(".nv-linesWrap").datum(l.filter(function(a){return!a.disabled&&!a.tempDisabled}));Z.call(f),l.forEach(function(a,b){a.seriesIndex=b});var $=l.filter(function(a){return!a.disabled&&!!B(a)}),_=W.select(".nv-avgLinesWrap").selectAll("line").data($,function(a){return a.key}),ab=function(a){var b=e(B(a));return 0>b?0:b>O?O:b};_.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(a){return f.color()(a,a.seriesIndex)}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.style("stroke-opacity",function(a){var b=e(B(a));return 0>b||b>O?0:1}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.exit().remove();var bb=Z.selectAll(".nv-indexLine").data([G]);bb.enter().append("rect").attr("class","nv-indexLine").attr("width",3).attr("x",-2).attr("fill","red").attr("fill-opacity",.5).style("pointer-events","all").call(Q),bb.attr("transform",function(a){return"translate("+F(a.i)+",0)"}).attr("height",O),r&&(g.scale(d)._ticks(a.utils.calcTicksX(N/70,l)).tickSize(-O,0),W.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),W.select(".nv-x.nv-axis").call(g)),s&&(h.scale(e)._ticks(a.utils.calcTicksY(O/36,l)).tickSize(-N,0),W.select(".nv-y.nv-axis").call(h)),W.select(".nv-background rect").on("click",function(){G.x=d3.mouse(this)[0],G.i=Math.round(F.invert(G.x)),y.index=G.i,C.stateChange(y),K()}),f.dispatch.on("elementClick",function(a){G.i=a.pointIndex,G.x=F(G.i),y.index=G.i,C.stateChange(y),K()}),j.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=!a.disabled,y.rescaleY=w,C.stateChange(y),b.update()}),i.dispatch.on("stateChange",function(a){for(var c in a)y[c]=a[c];C.stateChange(y),b.update()}),k.dispatch.on("elementMousemove",function(c){f.clearHighlights();var d,e,i,j=[];if(l.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g,h){e=a.interactiveBisect(g.values,c.pointXValue,b.x()),f.highlightPoint(h,e,!0);var k=g.values[e];"undefined"!=typeof k&&("undefined"==typeof d&&(d=k),"undefined"==typeof i&&(i=b.xScale()(b.x()(k,e))),j.push({key:g.key,value:b.y()(k,e),color:n(g,g.seriesIndex)}))}),j.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(j.map(function(a){return a.value}),o,q);null!==r&&(j[r].highlight=!0)}var s=g.tickFormat()(b.x()(d,e),e);k.tooltip.position({left:i+m.left,top:c.mouseY+m.top}).chartContainer(M.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:s,series:j})(),k.renderGuideLine(i)}),k.dispatch.on("elementMouseout",function(){f.clearHighlights()}),C.on("changeState",function(a){"undefined"!=typeof a.disabled&&(l.forEach(function(b,c){b.disabled=a.disabled[c]}),y.disabled=a.disabled),"undefined"!=typeof a.index&&(G.i=a.index,G.x=F(G.i),y.index=a.index,bb.data([G])),"undefined"!=typeof a.rescaleY&&(w=a.rescaleY),b.update()})}),H.renderEnd("cumulativeLineChart immediate"),b}function c(a,b){return K||(K=f.y()),b.map(function(b){if(!b.values)return b;var c=b.values[a];if(null==c)return b;var d=K(c,a);return-.95>d&&!E?(b.tempDisabled=!0,b):(b.tempDisabled=!1,b.values=b.values.map(function(a,b){return a.display={y:(K(a,b)-d)/(1+d)},a}),b)})}var d,e,f=a.models.line(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.models.legend(),k=a.interactiveGuideline(),l=a.models.tooltip(),m={top:30,right:30,bottom:50,left:60},n=a.utils.defaultColor(),o=null,p=null,q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=!0,x=f.id(),y=a.utils.state(),z=null,A=null,B=function(a){return a.average},C=d3.dispatch("stateChange","changeState","renderEnd"),D=250,E=!1;y.index=0,y.rescaleY=w,g.orient("bottom").tickPadding(7),h.orient(t?"right":"left"),l.valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)}),j.updateState(!1);var F=d3.scale.linear(),G={i:0,x:0},H=a.utils.renderWatch(C,D),I=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),index:G.i,rescaleY:w}}},J=function(a){return function(b){void 0!==b.index&&(G.i=b.index),void 0!==b.rescaleY&&(w=b.rescaleY),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){var c={x:b.x()(a.point),y:b.y()(a.point),color:a.point.color};a.point=c,l.data(a).position(a.pos).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){l.hidden(!0)});var K=null;return b.dispatch=C,b.lines=f,b.legend=i,b.controls=j,b.xAxis=g,b.yAxis=h,b.interactiveLayer=k,b.state=y,b.tooltip=l,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return o},set:function(a){o=a}},height:{get:function(){return p},set:function(a){p=a}},rescaleY:{get:function(){return w},set:function(a){w=a}},showControls:{get:function(){return u},set:function(a){u=a}},showLegend:{get:function(){return q},set:function(a){q=a}},average:{get:function(){return B},set:function(a){B=a}},defaultState:{get:function(){return z},set:function(a){z=a}},noData:{get:function(){return A},set:function(a){A=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},noErrorCheck:{get:function(){return E},set:function(a){E=a}},tooltips:{get:function(){return l.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),l.enabled(!!b)}},tooltipContent:{get:function(){return l.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),l.contentGenerator(b)}},margin:{get:function(){return m},set:function(a){m.top=void 0!==a.top?a.top:m.top,m.right=void 0!==a.right?a.right:m.right,m.bottom=void 0!==a.bottom?a.bottom:m.bottom,m.left=void 0!==a.left?a.left:m.left}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),i.color(n)}},useInteractiveGuideline:{get:function(){return v},set:function(a){v=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1))}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,h.orient(a?"right":"left")}},duration:{get:function(){return D},set:function(a){D=a,f.duration(D),g.duration(D),h.duration(D),H.reset(D)}}}),a.utils.inheritOptions(b,f),a.utils.initOptions(b),b},a.models.discreteBar=function(){"use strict";function b(m){return y.reset(),m.each(function(b){var m=k-j.left-j.right,x=l-j.top-j.bottom;c=d3.select(this),a.utils.initSVG(c),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var z=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0}})});n.domain(d||d3.merge(z).map(function(a){return a.x})).rangeBands(f||[0,m],.1),o.domain(e||d3.extent(d3.merge(z).map(function(a){return a.y}).concat(r))),o.range(t?g||[x-(o.domain()[0]<0?12:0),o.domain()[1]>0?12:0]:g||[x,0]),h=h||n,i=i||o.copy().range([o(0),o(0)]);{var A=c.selectAll("g.nv-wrap.nv-discretebar").data([b]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar"),C=B.append("g");A.select("g")}C.append("g").attr("class","nv-groups"),A.attr("transform","translate("+j.left+","+j.top+")");var D=A.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});D.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),D.exit().watchTransition(y,"discreteBar: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),D.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),D.watchTransition(y,"discreteBar: groups").style("stroke-opacity",1).style("fill-opacity",.75);var E=D.selectAll("g.nv-bar").data(function(a){return a.values});E.exit().remove();var F=E.enter().append("g").attr("transform",function(a,b){return"translate("+(n(p(a,b))+.05*n.rangeBand())+", "+o(0)+")"}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),v.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),v.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){v.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){v.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()});F.append("rect").attr("height",0).attr("width",.9*n.rangeBand()/b.length),t?(F.append("text").attr("text-anchor","middle"),E.select("text").text(function(a,b){return u(q(a,b))}).watchTransition(y,"discreteBar: bars text").attr("x",.9*n.rangeBand()/2).attr("y",function(a,b){return q(a,b)<0?o(q(a,b))-o(0)+12:-4})):E.selectAll("text").remove(),E.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(a,b){return a.color||s(a,b)}).style("stroke",function(a,b){return a.color||s(a,b)}).select("rect").attr("class",w).watchTransition(y,"discreteBar: bars rect").attr("width",.9*n.rangeBand()/b.length),E.watchTransition(y,"discreteBar: bars").attr("transform",function(a,b){var c=n(p(a,b))+.05*n.rangeBand(),d=q(a,b)<0?o(0):o(0)-o(q(a,b))<1?o(0)-1:o(q(a,b));return"translate("+c+", "+d+")"}).select("rect").attr("height",function(a,b){return Math.max(Math.abs(o(q(a,b))-o(e&&e[0]||0))||1)}),h=n.copy(),i=o.copy()}),y.renderEnd("discreteBar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=d3.scale.ordinal(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=a.utils.defaultColor(),t=!1,u=d3.format(",.2f"),v=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),w="discreteBar",x=250,y=a.utils.renderWatch(v,x);return b.dispatch=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},forceY:{get:function(){return r},set:function(a){r=a}},showValues:{get:function(){return t},set:function(a){t=a}},x:{get:function(){return p},set:function(a){p=a}},y:{get:function(){return q},set:function(a){q=a}},xScale:{get:function(){return n},set:function(a){n=a}},yScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},valueFormat:{get:function(){return u},set:function(a){u=a}},id:{get:function(){return m},set:function(a){m=a}},rectClass:{get:function(){return w},set:function(a){w=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b)}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x)}}}),a.utils.initOptions(b),b},a.models.discreteBarChart=function(){"use strict";function b(h){return t.reset(),t.models(e),m&&t.models(f),n&&t.models(g),h.each(function(h){var l=d3.select(this);a.utils.initSVG(l);var q=a.utils.availableWidth(j,l,i),t=a.utils.availableHeight(k,l,i);if(b.update=function(){r.beforeUpdate(),l.transition().duration(s).call(b)},b.container=this,!(h&&h.length&&h.filter(function(a){return a.values.length}).length))return a.utils.noData(b,l),b;l.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var u=l.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([h]),v=u.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g"),w=v.append("defs"),x=u.select("g");v.append("g").attr("class","nv-x nv-axis"),v.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),v.append("g").attr("class","nv-barsWrap"),x.attr("transform","translate("+i.left+","+i.top+")"),o&&x.select(".nv-y.nv-axis").attr("transform","translate("+q+",0)"),e.width(q).height(t);var y=x.select(".nv-barsWrap").datum(h.filter(function(a){return!a.disabled}));if(y.transition().call(e),w.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),x.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(p?2:1)).attr("height",16).attr("x",-c.rangeBand()/(p?1:2)),m){f.scale(c)._ticks(a.utils.calcTicksX(q/100,h)).tickSize(-t,0),x.select(".nv-x.nv-axis").attr("transform","translate(0,"+(d.range()[0]+(e.showValues()&&d.domain()[0]<0?16:0))+")"),x.select(".nv-x.nv-axis").call(f); +var z=x.select(".nv-x.nv-axis").selectAll("g");p&&z.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}n&&(g.scale(d)._ticks(a.utils.calcTicksY(t/36,h)).tickSize(-q,0),x.select(".nv-y.nv-axis").call(g)),x.select(".nv-zeroLine line").attr("x1",0).attr("x2",q).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("discreteBar chart immediate"),b}var c,d,e=a.models.discreteBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.tooltip(),i={top:15,right:10,bottom:50,left:60},j=null,k=null,l=a.utils.getColor(),m=!0,n=!0,o=!1,p=!1,q=null,r=d3.dispatch("beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(o?"right":"left").tickFormat(d3.format(",.1f")),h.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).keyFormatter(function(a,b){return f.tickFormat()(a,b)});var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},h.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){h.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){h.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.discretebar=e,b.xAxis=f,b.yAxis=g,b.tooltip=h,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},staggerLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return m},set:function(a){m=a}},showYAxis:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return q},set:function(a){q=a}},tooltips:{get:function(){return h.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),h.enabled(!!b)}},tooltipContent:{get:function(){return h.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),h.contentGenerator(b)}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),e.color(l)}},rightAlignYAxis:{get:function(){return o},set:function(a){o=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.distribution=function(){"use strict";function b(k){return m.reset(),k.each(function(b){var k=(e-("x"===g?d.left+d.right:d.top+d.bottom),"x"==g?"y":"x"),l=d3.select(this);a.utils.initSVG(l),c=c||j;var n=l.selectAll("g.nv-distribution").data([b]),o=n.enter().append("g").attr("class","nvd3 nv-distribution"),p=(o.append("g"),n.select("g"));n.attr("transform","translate("+d.left+","+d.top+")");var q=p.selectAll("g.nv-dist").data(function(a){return a},function(a){return a.key});q.enter().append("g"),q.attr("class",function(a,b){return"nv-dist nv-series-"+b}).style("stroke",function(a,b){return i(a,b)});var r=q.selectAll("line.nv-dist"+g).data(function(a){return a.values});r.enter().append("line").attr(g+"1",function(a,b){return c(h(a,b))}).attr(g+"2",function(a,b){return c(h(a,b))}),m.transition(q.exit().selectAll("line.nv-dist"+g),"dist exit").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}).style("stroke-opacity",0).remove(),r.attr("class",function(a,b){return"nv-dist"+g+" nv-dist"+g+"-"+b}).attr(k+"1",0).attr(k+"2",f),m.transition(r,"dist").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}),c=j.copy()}),m.renderEnd("distribution immediate"),b}var c,d={top:0,right:0,bottom:0,left:0},e=400,f=8,g="x",h=function(a){return a[g]},i=a.utils.defaultColor(),j=d3.scale.linear(),k=250,l=d3.dispatch("renderEnd"),m=a.utils.renderWatch(l,k);return b.options=a.utils.optionsFunc.bind(b),b.dispatch=l,b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.axis=function(a){return arguments.length?(g=a,b):g},b.size=function(a){return arguments.length?(f=a,b):f},b.getData=function(a){return arguments.length?(h=d3.functor(a),b):h},b.scale=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),b):i},b.duration=function(a){return arguments.length?(k=a,m.reset(k),b):k},b},a.models.furiousLegend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?g(a,b):"#fff":m?void 0:a.disabled?g(a,b):"#fff"}function r(a,b){return m&&"furious"==o?a.disengaged?"#fff":g(a,b):a.disabled?"#fff":g(a,b)}return p.each(function(b){var p=d-c.left-c.right,s=d3.select(this);a.utils.initSVG(s);var t=s.selectAll("g.nv-legend").data([b]),u=(t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),t.select("g"));t.attr("transform","translate("+c.left+","+c.top+")");var v,w=u.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),x=w.enter().append("g").attr("class","nv-series");if("classic"==o)x.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),v=w.select("circle");else if("furious"==o){x.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),v=w.select("rect"),x.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var y=w.select(".nv-check-box");y.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}x.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var z=w.select("text.nv-legend-text");w.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=w.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=w.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),w.classed("nv-disabled",function(a){return a.userDisabled}),w.exit().remove(),z.attr("fill",q).text(f);var A;switch(o){case"furious":A=23;break;case"classic":A=20}if(h){var B=[];w.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}B.push(b+i)});for(var C=0,D=0,E=[];p>D&&Cp&&C>1;){E=[],C--;for(var F=0;F(E[F%C]||0)&&(E[F%C]=B[F]);D=E.reduce(function(a,b){return a+b})}for(var G=[],H=0,I=0;C>H;H++)G[H]=I,I+=E[H];w.attr("transform",function(a,b){return"translate("+G[b%C]+","+(5+Math.floor(b/C)*A)+")"}),j?u.attr("transform","translate("+(d-c.right-D)+","+c.top+")"):u.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(B.length/C)*A}else{var J,K=5,L=5,M=0;w.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return J=L,dM&&(M=L),"translate("+J+","+K+")"}),u.attr("transform","translate("+(d-c.right-M)+","+c.top+")"),e=c.top+c.bottom+K+15}"furious"==o&&v.attr("width",function(a,b){return z[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),v.style("fill",r).style("stroke",function(a,b){return a.color||g(a,b)})}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=28,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBar=function(){"use strict";function b(x){return x.each(function(b){w.reset(),k=d3.select(this);var x=a.utils.availableWidth(h,k,g),y=a.utils.availableHeight(i,k,g);a.utils.initSVG(k),l.domain(c||d3.extent(b[0].values.map(n).concat(p))),l.range(r?e||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:e||[0,x]),m.domain(d||d3.extent(b[0].values.map(o).concat(q))).range(f||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var z=k.selectAll("g.nv-wrap.nv-historicalBar-"+j).data([b[0].values]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+j),B=A.append("defs"),C=A.append("g"),D=z.select("g");C.append("g").attr("class","nv-bars"),z.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){u.chartClick({data:a,index:b,pos:d3.event,id:j})}),B.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),z.select("#nv-chart-clip-path-"+j+" rect").attr("width",x).attr("height",y),D.attr("clip-path",s?"url(#nv-chart-clip-path-"+j+")":"");var E=z.select(".nv-bars").selectAll(".nv-bar").data(function(a){return a},function(a,b){return n(a,b)});E.exit().remove(),E.enter().append("rect").attr("x",0).attr("y",function(b,c){return a.utils.NaNtoZero(m(Math.max(0,o(b,c))))}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.abs(m(o(b,c))-m(0)))}).attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).on("mouseover",function(a,b){v&&(d3.select(this).classed("hover",!0),u.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mouseout",function(a,b){v&&(d3.select(this).classed("hover",!1),u.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mousemove",function(a,b){v&&u.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v&&(u.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}).on("dblclick",function(a,b){v&&(u.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}),E.attr("fill",function(a,b){return t(a,b)}).attr("class",function(a,b,c){return(o(a,b)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+c+"-"+b}).watchTransition(w,"bars").attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).attr("width",x/b[0].values.length*.9),E.watchTransition(w,"bars").attr("y",function(b,c){var d=o(b,c)<0?m(0):m(0)-m(o(b,c))<1?m(0)-1:m(o(b,c));return a.utils.NaNtoZero(d)}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.max(Math.abs(m(o(b,c))-m(0)),1))})}),w.renderEnd("historicalBar immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=[],q=[0],r=!1,s=!0,t=a.utils.defaultColor(),u=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),v=!0,w=a.utils.renderWatch(u,0);return b.highlightPoint=function(a,b){k.select(".nv-bars .nv-bar-0-"+a).classed("hover",b)},b.clearHighlights=function(){k.select(".nv-bars .nv-bar.hover").classed("hover",!1)},b.dispatch=u,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},forceX:{get:function(){return p},set:function(a){p=a}},forceY:{get:function(){return q},set:function(a){q=a}},padData:{get:function(){return r},set:function(a){r=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},clipEdge:{get:function(){return s},set:function(a){s=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return v},set:function(a){v=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return t},set:function(b){t=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBarChart=function(b){"use strict";function c(b){return b.each(function(k){z.reset(),z.models(f),q&&z.models(g),r&&z.models(h);var w=d3.select(this),A=this;a.utils.initSVG(w);var B=a.utils.availableWidth(n,w,l),C=a.utils.availableHeight(o,w,l);if(c.update=function(){w.transition().duration(y).call(c)},c.container=this,u.disabled=k.map(function(a){return!!a.disabled}),!v){var D;v={};for(D in u)v[D]=u[D]instanceof Array?u[D].slice(0):u[D]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(c,w),c;w.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale();var E=w.selectAll("g.nv-wrap.nv-historicalBarChart").data([k]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g"),G=E.select("g");F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-barsWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),p&&(i.width(B),G.select(".nv-legendWrap").datum(k).call(i),l.top!=i.height()&&(l.top=i.height(),C=a.utils.availableHeight(o,w,l)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-l.top+")")),E.attr("transform","translate("+l.left+","+l.top+")"),s&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),t&&(j.width(B).height(C).margin({left:l.left,top:l.top}).svgContainer(w).xScale(d),E.select(".nv-interactive").call(j)),f.width(B).height(C).color(k.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!k[b].disabled}));var H=G.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));H.transition().call(f),q&&(g.scale(d)._ticks(a.utils.calcTicksX(B/100,k)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),G.select(".nv-x.nv-axis").transition().call(g)),r&&(h.scale(e)._ticks(a.utils.calcTicksY(C/36,k)).tickSize(-B,0),G.select(".nv-y.nv-axis").transition().call(h)),j.dispatch.on("elementMousemove",function(b){f.clearHighlights();var d,e,i,n=[];k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g){e=a.interactiveBisect(g.values,b.pointXValue,c.x()),f.highlightPoint(e,!0);var h=g.values[e];void 0!==h&&(void 0===d&&(d=h),void 0===i&&(i=c.xScale()(c.x()(h,e))),n.push({key:g.key,value:c.y()(h,e),color:m(g,g.seriesIndex),data:g.values[e]}))});var o=g.tickFormat()(c.x()(d,e));j.tooltip.position({left:i+l.left,top:b.mouseY+l.top}).chartContainer(A.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:o,index:e,series:n})(),j.renderGuideLine(i)}),j.dispatch.on("elementMouseout",function(){x.tooltipHide(),f.clearHighlights()}),i.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,k.filter(function(a){return!a.disabled}).length||k.map(function(a){return a.disabled=!1,E.selectAll(".nv-series").classed("disabled",!1),a}),u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),b.transition().call(c)}),i.dispatch.on("legendDblclick",function(a){k.forEach(function(a){a.disabled=!0}),a.disabled=!1,u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),c.update()}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),c.update()})}),z.renderEnd("historicalBarChart immediate"),c}var d,e,f=b||a.models.historicalBar(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:90,bottom:50,left:90},m=a.utils.defaultColor(),n=null,o=null,p=!1,q=!0,r=!0,s=!1,t=!1,u={},v=null,w=null,x=d3.dispatch("tooltipHide","stateChange","changeState","renderEnd"),y=250;g.orient("bottom").tickPadding(7),h.orient(s?"right":"left"),k.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)});var z=a.utils.renderWatch(x,0);return f.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:c.x()(a.data),value:c.y()(a.data),color:a.color},k.data(a).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),f.dispatch.on("elementMousemove.tooltip",function(){k.position({top:d3.event.pageY,left:d3.event.pageX})()}),c.dispatch=x,c.bars=f,c.legend=i,c.xAxis=g,c.yAxis=h,c.interactiveLayer=j,c.tooltip=k,c.options=a.utils.optionsFunc.bind(c),c._options=Object.create({},{width:{get:function(){return n},set:function(a){n=a}},height:{get:function(){return o},set:function(a){o=a}},showLegend:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return q},set:function(a){q=a}},showYAxis:{get:function(){return r},set:function(a){r=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b),i.color(m),f.color(m)}},duration:{get:function(){return y},set:function(a){y=a,z.reset(y),h.duration(y),g.duration(y)}},rightAlignYAxis:{get:function(){return s},set:function(a){s=a,h.orient(a?"right":"left")}},useInteractiveGuideline:{get:function(){return t},set:function(a){t=a,a===!0&&c.interactive(!1)}}}),a.utils.inheritOptions(c,f),a.utils.initOptions(c),c},a.models.ohlcBarChart=function(){var b=a.models.historicalBarChart(a.models.ohlcBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
open:"+b.yAxis.tickFormat()(c.open)+"
close:"+b.yAxis.tickFormat()(c.close)+"
high"+b.yAxis.tickFormat()(c.high)+"
low:"+b.yAxis.tickFormat()(c.low)+"
"}),b},a.models.candlestickBarChart=function(){var b=a.models.historicalBarChart(a.models.candlestickBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
open:"+b.yAxis.tickFormat()(c.open)+"
close:"+b.yAxis.tickFormat()(c.close)+"
high"+b.yAxis.tickFormat()(c.high)+"
low:"+b.yAxis.tickFormat()(c.low)+"
"}),b},a.models.legend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?"#000":"#fff":m?void 0:(a.color||(a.color=g(a,b)),a.disabled?a.color:"#fff")}function r(a,b){return m&&"furious"==o&&a.disengaged?"#eee":a.color||g(a,b)}function s(a){return m&&"furious"==o?1:a.disabled?0:1}return p.each(function(b){var g=d-c.left-c.right,p=d3.select(this);a.utils.initSVG(p);var t=p.selectAll("g.nv-legend").data([b]),u=t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),v=t.select("g");t.attr("transform","translate("+c.left+","+c.top+")");var w,x,y=v.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),z=y.enter().append("g").attr("class","nv-series");switch(o){case"furious":x=23;break;case"classic":x=20}if("classic"==o)z.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),w=y.select("circle");else if("furious"==o){z.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),w=y.select(".nv-legend-symbol"),z.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var A=y.select(".nv-check-box");A.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}z.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var B=y.select("text.nv-legend-text");y.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=y.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=y.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),y.classed("nv-disabled",function(a){return a.userDisabled}),y.exit().remove(),B.attr("fill",q).text(f);var C=0;if(h){var D=[];y.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}D.push(b+i)});var E=0,F=[];for(C=0;g>C&&Eg&&E>1;){F=[],E--;for(var G=0;G(F[G%E]||0)&&(F[G%E]=D[G]);C=F.reduce(function(a,b){return a+b})}for(var H=[],I=0,J=0;E>I;I++)H[I]=J,J+=F[I];y.attr("transform",function(a,b){return"translate("+H[b%E]+","+(5+Math.floor(b/E)*x)+")"}),j?v.attr("transform","translate("+(d-c.right-C)+","+c.top+")"):v.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(D.length/E)*x}else{var K,L=5,M=5,N=0;y.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return K=M,dN&&(N=M),K+N>C&&(C=K+N),"translate("+K+","+L+")"}),v.attr("transform","translate("+(d-c.right-N)+","+c.top+")"),e=c.top+c.bottom+L+15}if("furious"==o){w.attr("width",function(a,b){return B[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),u.insert("rect",":first-child").attr("class","nv-legend-bg").attr("fill","#eee").attr("opacity",0);var O=v.select(".nv-legend-bg");O.transition().duration(300).attr("x",-x).attr("width",C+x-12).attr("height",e+10).attr("y",-c.top-10).attr("opacity",m?1:0)}w.style("fill",r).style("fill-opacity",s).style("stroke",r)}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=32,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.line=function(){"use strict";function b(r){return v.reset(),v.models(e),r.each(function(b){i=d3.select(this);var r=a.utils.availableWidth(g,i,f),s=a.utils.availableHeight(h,i,f);a.utils.initSVG(i),c=e.xScale(),d=e.yScale(),t=t||c,u=u||d;var w=i.selectAll("g.nv-wrap.nv-line").data([b]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-line"),y=x.append("defs"),z=x.append("g"),A=w.select("g");z.append("g").attr("class","nv-groups"),z.append("g").attr("class","nv-scatterWrap"),w.attr("transform","translate("+f.left+","+f.top+")"),e.width(r).height(s);var B=w.select(".nv-scatterWrap");B.call(e),y.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect"),w.select("#nv-edge-clip-"+e.id()+" rect").attr("width",r).attr("height",s>0?s:0),A.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":""),B.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":"");var C=w.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});C.enter().append("g").style("stroke-opacity",1e-6).style("stroke-width",function(a){return a.strokeWidth||j}).style("fill-opacity",1e-6),C.exit().remove(),C.attr("class",function(a,b){return(a.classed||"")+" nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return k(a,b)}).style("stroke",function(a,b){return k(a,b)}),C.watchTransition(v,"line: groups").style("stroke-opacity",1).style("fill-opacity",function(a){return a.fillOpacity||.5});var D=C.selectAll("path.nv-area").data(function(a){return o(a)?[a]:[]});D.enter().append("path").attr("class","nv-area").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y0(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))}).y1(function(){return u(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])}),C.exit().selectAll("path.nv-area").remove(),D.watchTransition(v,"line: areaPaths").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y0(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))}).y1(function(){return d(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])});var E=C.selectAll("path.nv-line").data(function(a){return[a.values]});E.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))})),E.watchTransition(v,"line: linePaths").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))})),t=c.copy(),u=d.copy()}),v.renderEnd("line immediate"),b}var c,d,e=a.models.scatter(),f={top:0,right:0,bottom:0,left:0},g=960,h=500,i=null,j=1.5,k=a.utils.defaultColor(),l=function(a){return a.x},m=function(a){return a.y},n=function(a,b){return!isNaN(m(a,b))&&null!==m(a,b)},o=function(a){return a.area},p=!1,q="linear",r=250,s=d3.dispatch("elementClick","elementMouseover","elementMouseout","renderEnd");e.pointSize(16).pointDomain([16,256]);var t,u,v=a.utils.renderWatch(s,r);return b.dispatch=s,b.scatter=e,e.dispatch.on("elementClick",function(){s.elementClick.apply(this,arguments)}),e.dispatch.on("elementMouseover",function(){s.elementMouseover.apply(this,arguments)}),e.dispatch.on("elementMouseout",function(){s.elementMouseout.apply(this,arguments)}),b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},defined:{get:function(){return n},set:function(a){n=a}},interpolate:{get:function(){return q},set:function(a){q=a}},clipEdge:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}},duration:{get:function(){return r},set:function(a){r=a,v.reset(r),e.duration(r)}},isArea:{get:function(){return o},set:function(a){o=d3.functor(a)}},x:{get:function(){return l},set:function(a){l=a,e.x(a)}},y:{get:function(){return m},set:function(a){m=a,e.y(a)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.lineChart=function(){"use strict";function b(j){return y.reset(),y.models(e),p&&y.models(f),q&&y.models(g),j.each(function(j){var v=d3.select(this),y=this;a.utils.initSVG(v);var B=a.utils.availableWidth(m,v,k),C=a.utils.availableHeight(n,v,k);if(b.update=function(){0===x?v.call(b):v.transition().duration(x).call(b)},b.container=this,t.setter(A(j),b.update).getter(z(j)).update(),t.disabled=j.map(function(a){return!!a.disabled}),!u){var D;u={};for(D in t)u[D]=t[D]instanceof Array?t[D].slice(0):t[D] +}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,v),b;v.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var E=v.selectAll("g.nv-wrap.nv-lineChart").data([j]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g"),G=E.select("g");F.append("rect").style("opacity",0),F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-linesWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),G.select("rect").attr("width",B).attr("height",C>0?C:0),o&&(h.width(B),G.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),C=a.utils.availableHeight(n,v,k)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-k.top+")")),E.attr("transform","translate("+k.left+","+k.top+")"),r&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),s&&(i.width(B).height(C).margin({left:k.left,top:k.top}).svgContainer(v).xScale(c),E.select(".nv-interactive").call(i)),e.width(B).height(C).color(j.map(function(a,b){return a.color||l(a,b)}).filter(function(a,b){return!j[b].disabled}));var H=G.select(".nv-linesWrap").datum(j.filter(function(a){return!a.disabled}));H.call(e),p&&(f.scale(c)._ticks(a.utils.calcTicksX(B/100,j)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),G.select(".nv-x.nv-axis").call(f)),q&&(g.scale(d)._ticks(a.utils.calcTicksY(C/36,j)).tickSize(-B,0),G.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)t[c]=a[c];w.stateChange(t),b.update()}),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,m,n=[];if(j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x());var i=f.values[h],j=b.y()(i,h);null!=j&&e.highlightPoint(g,h,!0),void 0!==i&&(void 0===d&&(d=i),void 0===m&&(m=b.xScale()(b.x()(i,h))),n.push({key:f.key,value:j,color:l(f,f.seriesIndex)}))}),n.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(n.map(function(a){return a.value}),o,q);null!==r&&(n[r].highlight=!0)}var s=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:c.mouseX+k.left,top:c.mouseY+k.top}).chartContainer(y.parentNode).valueFormatter(function(a){return null==a?"N/A":g.tickFormat()(a)}).data({value:s,index:h,series:n})(),i.renderGuideLine(m)}),i.dispatch.on("elementClick",function(c){var d,f=[];j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(e){var g=a.interactiveBisect(e.values,c.pointXValue,b.x()),h=e.values[g];if("undefined"!=typeof h){"undefined"==typeof d&&(d=b.xScale()(b.x()(h,g)));var i=b.yScale()(b.y()(h,g));f.push({point:h,pointIndex:g,pos:[d,i],seriesIndex:e.seriesIndex,series:e})}}),e.dispatch.elementClick(f)}),i.dispatch.on("elementMouseout",function(){e.clearHighlights()}),w.on("changeState",function(a){"undefined"!=typeof a.disabled&&j.length===a.disabled.length&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),t.disabled=a.disabled),b.update()})}),y.renderEnd("lineChart immediate"),b}var c,d,e=a.models.line(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=a.utils.defaultColor(),m=null,n=null,o=!0,p=!0,q=!0,r=!1,s=!1,t=a.utils.state(),u=null,v=null,w=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),x=250;f.orient("bottom").tickPadding(7),g.orient(r?"right":"left"),j.valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)});var y=a.utils.renderWatch(w,x),z=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},A=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){j.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),b.dispatch=w,b.lines=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,b.tooltip=j,b.dispatch=w,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return p},set:function(a){p=a}},showYAxis:{get:function(){return q},set:function(a){q=a}},defaultState:{get:function(){return u},set:function(a){u=a}},noData:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x),e.duration(x),f.duration(x),g.duration(x)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),h.color(l),e.color(l)}},rightAlignYAxis:{get:function(){return r},set:function(a){r=a,g.orient(r?"right":"left")}},useInteractiveGuideline:{get:function(){return s},set:function(a){s=a,s&&(e.interactive(!1),e.useVoronoi(!1))}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.linePlusBarChart=function(){"use strict";function b(v){return v.each(function(v){function J(a){var b=+("e"==a),c=b?1:-1,d=X/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function S(){u.empty()||u.extent(I),kb.data([u.empty()?e.domain():I]).each(function(a){var b=e(a[0])-e.range()[0],c=e.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>c?0:c)})}function T(){I=u.empty()?null:u.extent(),c=u.empty()?e.domain():u.extent(),K.brush({extent:c,brush:u}),S(),l.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),j.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var b=db.select(".nv-focus .nv-barsWrap").datum(Z.length?Z.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return l.x()(a,b)>=c[0]&&l.x()(a,b)<=c[1]})}}):[{values:[]}]),h=db.select(".nv-focus .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$.map(function(a){return{area:a.area,fillOpacity:a.fillOpacity,key:a.key,values:a.values.filter(function(a,b){return j.x()(a,b)>=c[0]&&j.x()(a,b)<=c[1]})}}));d=Z.length?l.xScale():j.xScale(),n.scale(d)._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-W,0),n.domain([Math.ceil(c[0]),Math.floor(c[1])]),db.select(".nv-x.nv-axis").transition().duration(L).call(n),b.transition().duration(L).call(l),h.transition().duration(L).call(j),db.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),p.scale(f)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(-V,0),q.scale(g)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(Z.length?0:-V,0),db.select(".nv-focus .nv-y1.nv-axis").style("opacity",Z.length?1:0),db.select(".nv-focus .nv-y2.nv-axis").style("opacity",$.length&&!$[0].disabled?1:0).attr("transform","translate("+d.range()[1]+",0)"),db.select(".nv-focus .nv-y1.nv-axis").transition().duration(L).call(p),db.select(".nv-focus .nv-y2.nv-axis").transition().duration(L).call(q)}var U=d3.select(this);a.utils.initSVG(U);var V=a.utils.availableWidth(y,U,w),W=a.utils.availableHeight(z,U,w)-(E?H:0),X=H-x.top-x.bottom;if(b.update=function(){U.transition().duration(L).call(b)},b.container=this,M.setter(R(v),b.update).getter(Q(v)).update(),M.disabled=v.map(function(a){return!!a.disabled}),!N){var Y;N={};for(Y in M)N[Y]=M[Y]instanceof Array?M[Y].slice(0):M[Y]}if(!(v&&v.length&&v.filter(function(a){return a.values.length}).length))return a.utils.noData(b,U),b;U.selectAll(".nv-noData").remove();var Z=v.filter(function(a){return!a.disabled&&a.bar}),$=v.filter(function(a){return!a.bar});d=l.xScale(),e=o.scale(),f=l.yScale(),g=j.yScale(),h=m.yScale(),i=k.yScale();var _=v.filter(function(a){return!a.disabled&&a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})}),ab=v.filter(function(a){return!a.disabled&&!a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})});d.range([0,V]),e.domain(d3.extent(d3.merge(_.concat(ab)),function(a){return a.x})).range([0,V]);var bb=U.selectAll("g.nv-wrap.nv-linePlusBar").data([v]),cb=bb.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),db=bb.select("g");cb.append("g").attr("class","nv-legendWrap");var eb=cb.append("g").attr("class","nv-focus");eb.append("g").attr("class","nv-x nv-axis"),eb.append("g").attr("class","nv-y1 nv-axis"),eb.append("g").attr("class","nv-y2 nv-axis"),eb.append("g").attr("class","nv-barsWrap"),eb.append("g").attr("class","nv-linesWrap");var fb=cb.append("g").attr("class","nv-context");if(fb.append("g").attr("class","nv-x nv-axis"),fb.append("g").attr("class","nv-y1 nv-axis"),fb.append("g").attr("class","nv-y2 nv-axis"),fb.append("g").attr("class","nv-barsWrap"),fb.append("g").attr("class","nv-linesWrap"),fb.append("g").attr("class","nv-brushBackground"),fb.append("g").attr("class","nv-x nv-brush"),D){var gb=t.align()?V/2:V,hb=t.align()?gb:0;t.width(gb),db.select(".nv-legendWrap").datum(v.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?O:P),a})).call(t),w.top!=t.height()&&(w.top=t.height(),W=a.utils.availableHeight(z,U,w)-H),db.select(".nv-legendWrap").attr("transform","translate("+hb+","+-w.top+")")}bb.attr("transform","translate("+w.left+","+w.top+")"),db.select(".nv-context").style("display",E?"initial":"none"),m.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),k.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var ib=db.select(".nv-context .nv-barsWrap").datum(Z.length?Z:[{values:[]}]),jb=db.select(".nv-context .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$);db.select(".nv-context").attr("transform","translate(0,"+(W+w.bottom+x.top)+")"),ib.transition().call(m),jb.transition().call(k),G&&(o._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-X,0),db.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+h.range()[0]+")"),db.select(".nv-context .nv-x.nv-axis").transition().call(o)),F&&(r.scale(h)._ticks(X/36).tickSize(-V,0),s.scale(i)._ticks(X/36).tickSize(Z.length?0:-V,0),db.select(".nv-context .nv-y3.nv-axis").style("opacity",Z.length?1:0).attr("transform","translate(0,"+e.range()[0]+")"),db.select(".nv-context .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+e.range()[1]+",0)"),db.select(".nv-context .nv-y1.nv-axis").transition().call(r),db.select(".nv-context .nv-y2.nv-axis").transition().call(s)),u.x(e).on("brush",T),I&&u.extent(I);var kb=db.select(".nv-brushBackground").selectAll("g").data([I||u.extent()]),lb=kb.enter().append("g");lb.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",X),lb.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",X);var mb=db.select(".nv-x.nv-brush").call(u);mb.selectAll("rect").attr("height",X),mb.selectAll(".resize").append("path").attr("d",J),t.dispatch.on("stateChange",function(a){for(var c in a)M[c]=a[c];K.stateChange(M),b.update()}),K.on("changeState",function(a){"undefined"!=typeof a.disabled&&(v.forEach(function(b,c){b.disabled=a.disabled[c]}),M.disabled=a.disabled),b.update()}),T()}),b}var c,d,e,f,g,h,i,j=a.models.line(),k=a.models.line(),l=a.models.historicalBar(),m=a.models.historicalBar(),n=a.models.axis(),o=a.models.axis(),p=a.models.axis(),q=a.models.axis(),r=a.models.axis(),s=a.models.axis(),t=a.models.legend(),u=d3.svg.brush(),v=a.models.tooltip(),w={top:30,right:30,bottom:30,left:60},x={top:0,right:30,bottom:20,left:60},y=null,z=null,A=function(a){return a.x},B=function(a){return a.y},C=a.utils.defaultColor(),D=!0,E=!0,F=!1,G=!0,H=50,I=null,J=null,K=d3.dispatch("brush","stateChange","changeState"),L=0,M=a.utils.state(),N=null,O=" (left axis)",P=" (right axis)";j.clipEdge(!0),k.interactive(!1),n.orient("bottom").tickPadding(5),p.orient("left"),q.orient("right"),o.orient("bottom").tickPadding(5),r.orient("left"),s.orient("right"),v.headerEnabled(!0).headerFormatter(function(a,b){return n.tickFormat()(a,b)});var Q=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},R=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return j.dispatch.on("elementMouseover.tooltip",function(a){v.duration(100).valueFormatter(function(a,b){return q.tickFormat()(a,b)}).data(a).position(a.pos).hidden(!1)}),j.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={value:b.y()(a.data),color:a.color},v.duration(0).valueFormatter(function(a,b){return p.tickFormat()(a,b)}).data(a).hidden(!1)}),l.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMousemove.tooltip",function(){v.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=K,b.legend=t,b.lines=j,b.lines2=k,b.bars=l,b.bars2=m,b.xAxis=n,b.x2Axis=o,b.y1Axis=p,b.y2Axis=q,b.y3Axis=r,b.y4Axis=s,b.tooltip=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return y},set:function(a){y=a}},height:{get:function(){return z},set:function(a){z=a}},showLegend:{get:function(){return D},set:function(a){D=a}},brushExtent:{get:function(){return I},set:function(a){I=a}},noData:{get:function(){return J},set:function(a){J=a}},focusEnable:{get:function(){return E},set:function(a){E=a}},focusHeight:{get:function(){return H},set:function(a){H=a}},focusShowAxisX:{get:function(){return G},set:function(a){G=a}},focusShowAxisY:{get:function(){return F},set:function(a){F=a}},legendLeftAxisHint:{get:function(){return O},set:function(a){O=a}},legendRightAxisHint:{get:function(){return P},set:function(a){P=a}},tooltips:{get:function(){return v.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),v.enabled(!!b)}},tooltipContent:{get:function(){return v.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),v.contentGenerator(b)}},margin:{get:function(){return w},set:function(a){w.top=void 0!==a.top?a.top:w.top,w.right=void 0!==a.right?a.right:w.right,w.bottom=void 0!==a.bottom?a.bottom:w.bottom,w.left=void 0!==a.left?a.left:w.left}},duration:{get:function(){return L},set:function(a){L=a}},color:{get:function(){return C},set:function(b){C=a.utils.getColor(b),t.color(C)}},x:{get:function(){return A},set:function(a){A=a,j.x(a),k.x(a),l.x(a),m.x(a)}},y:{get:function(){return B},set:function(a){B=a,j.y(a),k.y(a),l.y(a),m.y(a)}}}),a.utils.inheritOptions(b,j),a.utils.initOptions(b),b},a.models.lineWithFocusChart=function(){"use strict";function b(o){return o.each(function(o){function z(a){var b=+("e"==a),c=b?1:-1,d=M/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function G(){n.empty()||n.extent(y),U.data([n.empty()?e.domain():y]).each(function(a){var b=e(a[0])-c.range()[0],d=K-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>d?0:d)})}function H(){y=n.empty()?null:n.extent();var a=n.empty()?e.domain():n.extent();if(!(Math.abs(a[0]-a[1])<=1)){A.brush({extent:a,brush:n}),G();var b=Q.select(".nv-focus .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}).map(function(b){return{key:b.key,area:b.area,values:b.values.filter(function(b,c){return g.x()(b,c)>=a[0]&&g.x()(b,c)<=a[1]})}}));b.transition().duration(B).call(g),Q.select(".nv-focus .nv-x.nv-axis").transition().duration(B).call(i),Q.select(".nv-focus .nv-y.nv-axis").transition().duration(B).call(j)}}var I=d3.select(this),J=this;a.utils.initSVG(I);var K=a.utils.availableWidth(t,I,q),L=a.utils.availableHeight(u,I,q)-v,M=v-r.top-r.bottom;if(b.update=function(){I.transition().duration(B).call(b)},b.container=this,C.setter(F(o),b.update).getter(E(o)).update(),C.disabled=o.map(function(a){return!!a.disabled}),!D){var N;D={};for(N in C)D[N]=C[N]instanceof Array?C[N].slice(0):C[N]}if(!(o&&o.length&&o.filter(function(a){return a.values.length}).length))return a.utils.noData(b,I),b;I.selectAll(".nv-noData").remove(),c=g.xScale(),d=g.yScale(),e=h.xScale(),f=h.yScale();var O=I.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([o]),P=O.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g"),Q=O.select("g");P.append("g").attr("class","nv-legendWrap");var R=P.append("g").attr("class","nv-focus");R.append("g").attr("class","nv-x nv-axis"),R.append("g").attr("class","nv-y nv-axis"),R.append("g").attr("class","nv-linesWrap"),R.append("g").attr("class","nv-interactive");var S=P.append("g").attr("class","nv-context");S.append("g").attr("class","nv-x nv-axis"),S.append("g").attr("class","nv-y nv-axis"),S.append("g").attr("class","nv-linesWrap"),S.append("g").attr("class","nv-brushBackground"),S.append("g").attr("class","nv-x nv-brush"),x&&(m.width(K),Q.select(".nv-legendWrap").datum(o).call(m),q.top!=m.height()&&(q.top=m.height(),L=a.utils.availableHeight(u,I,q)-v),Q.select(".nv-legendWrap").attr("transform","translate(0,"+-q.top+")")),O.attr("transform","translate("+q.left+","+q.top+")"),w&&(p.width(K).height(L).margin({left:q.left,top:q.top}).svgContainer(I).xScale(c),O.select(".nv-interactive").call(p)),g.width(K).height(L).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),h.defined(g.defined()).width(K).height(M).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),Q.select(".nv-context").attr("transform","translate(0,"+(L+q.bottom+r.top)+")");var T=Q.select(".nv-context .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}));d3.transition(T).call(h),i.scale(c)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-L,0),j.scale(d)._ticks(a.utils.calcTicksY(L/36,o)).tickSize(-K,0),Q.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+L+")"),n.x(e).on("brush",function(){H()}),y&&n.extent(y);var U=Q.select(".nv-brushBackground").selectAll("g").data([y||n.extent()]),V=U.enter().append("g");V.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",M),V.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",M);var W=Q.select(".nv-x.nv-brush").call(n);W.selectAll("rect").attr("height",M),W.selectAll(".resize").append("path").attr("d",z),H(),k.scale(e)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-M,0),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),d3.transition(Q.select(".nv-context .nv-x.nv-axis")).call(k),l.scale(f)._ticks(a.utils.calcTicksY(M/36,o)).tickSize(-K,0),d3.transition(Q.select(".nv-context .nv-y.nv-axis")).call(l),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),m.dispatch.on("stateChange",function(a){for(var c in a)C[c]=a[c];A.stateChange(C),b.update()}),p.dispatch.on("elementMousemove",function(c){g.clearHighlights();var d,f,h,k=[];if(o.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(i,j){var l=n.empty()?e.domain():n.extent(),m=i.values.filter(function(a,b){return g.x()(a,b)>=l[0]&&g.x()(a,b)<=l[1]});f=a.interactiveBisect(m,c.pointXValue,g.x());var o=m[f],p=b.y()(o,f);null!=p&&g.highlightPoint(j,f,!0),void 0!==o&&(void 0===d&&(d=o),void 0===h&&(h=b.xScale()(b.x()(o,f))),k.push({key:i.key,value:b.y()(o,f),color:s(i,i.seriesIndex)}))}),k.length>2){var l=b.yScale().invert(c.mouseY),m=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),r=.03*m,t=a.nearestValueIndex(k.map(function(a){return a.value}),l,r);null!==t&&(k[t].highlight=!0)}var u=i.tickFormat()(b.x()(d,f));p.tooltip.position({left:c.mouseX+q.left,top:c.mouseY+q.top}).chartContainer(J.parentNode).valueFormatter(function(a){return null==a?"N/A":j.tickFormat()(a)}).data({value:u,index:f,series:k})(),p.renderGuideLine(h)}),p.dispatch.on("elementMouseout",function(){g.clearHighlights()}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&o.forEach(function(b,c){b.disabled=a.disabled[c]}),b.update()})}),b}var c,d,e,f,g=a.models.line(),h=a.models.line(),i=a.models.axis(),j=a.models.axis(),k=a.models.axis(),l=a.models.axis(),m=a.models.legend(),n=d3.svg.brush(),o=a.models.tooltip(),p=a.interactiveGuideline(),q={top:30,right:30,bottom:30,left:60},r={top:0,right:30,bottom:20,left:60},s=a.utils.defaultColor(),t=null,u=null,v=50,w=!1,x=!0,y=null,z=null,A=d3.dispatch("brush","stateChange","changeState"),B=250,C=a.utils.state(),D=null;g.clipEdge(!0).duration(0),h.interactive(!1),i.orient("bottom").tickPadding(5),j.orient("left"),k.orient("bottom").tickPadding(5),l.orient("left"),o.valueFormatter(function(a,b){return j.tickFormat()(a,b)}).headerFormatter(function(a,b){return i.tickFormat()(a,b)});var E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return g.dispatch.on("elementMouseover.tooltip",function(a){o.data(a).position(a.pos).hidden(!1)}),g.dispatch.on("elementMouseout.tooltip",function(){o.hidden(!0)}),b.dispatch=A,b.legend=m,b.lines=g,b.lines2=h,b.xAxis=i,b.yAxis=j,b.x2Axis=k,b.y2Axis=l,b.interactiveLayer=p,b.tooltip=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return t},set:function(a){t=a}},height:{get:function(){return u},set:function(a){u=a}},focusHeight:{get:function(){return v},set:function(a){v=a}},showLegend:{get:function(){return x},set:function(a){x=a}},brushExtent:{get:function(){return y},set:function(a){y=a}},defaultState:{get:function(){return D},set:function(a){D=a}},noData:{get:function(){return z},set:function(a){z=a}},tooltips:{get:function(){return o.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),o.enabled(!!b)}},tooltipContent:{get:function(){return o.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),o.contentGenerator(b)}},margin:{get:function(){return q},set:function(a){q.top=void 0!==a.top?a.top:q.top,q.right=void 0!==a.right?a.right:q.right,q.bottom=void 0!==a.bottom?a.bottom:q.bottom,q.left=void 0!==a.left?a.left:q.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b),m.color(s)}},interpolate:{get:function(){return g.interpolate()},set:function(a){g.interpolate(a),h.interpolate(a)}},xTickFormat:{get:function(){return i.tickFormat()},set:function(a){i.tickFormat(a),k.tickFormat(a)}},yTickFormat:{get:function(){return j.tickFormat()},set:function(a){j.tickFormat(a),l.tickFormat(a)}},duration:{get:function(){return B},set:function(a){B=a,j.duration(B),l.duration(B),i.duration(B),k.duration(B)}},x:{get:function(){return g.x()},set:function(a){g.x(a),h.x(a)}},y:{get:function(){return g.y()},set:function(a){g.y(a),h.y(a)}},useInteractiveGuideline:{get:function(){return w},set:function(a){w=a,w&&(g.interactive(!1),g.useVoronoi(!1))}}}),a.utils.inheritOptions(b,g),a.utils.initOptions(b),b},a.models.multiBar=function(){"use strict";function b(E){return C.reset(),E.each(function(b){var E=k-j.left-j.right,F=l-j.top-j.bottom;p=d3.select(this),a.utils.initSVG(p);var G=0;if(x&&b.length&&(x=[{values:b[0].values.map(function(a){return{x:a.x,y:0,series:a.series,size:.01}})}]),u){var H=d3.layout.stack().offset(v).values(function(a){return a.values}).y(r)(!b.length&&x?x:b);H.forEach(function(a,c){a.nonStackable?(b[c].nonStackableSeries=G++,H[c]=b[c]):c>0&&H[c-1].nonStackable&&H[c].values.map(function(a,b){a.y0-=H[c-1].values[b].y,a.y1=a.y0+a.y})}),b=H}b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),u&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a,f){if(!b[f].nonStackable){var g=a.values[c];g.size=Math.abs(g.y),g.y<0?(g.y1=e,e-=g.size):(g.y1=g.size+d,d+=g.size)}})});var I=d&&e?[]:b.map(function(a,b){return a.values.map(function(a,c){return{x:q(a,c),y:r(a,c),y0:a.y0,y1:a.y1,idx:b}})});m.domain(d||d3.merge(I).map(function(a){return a.x})).rangeBands(f||[0,E],A),n.domain(e||d3.extent(d3.merge(I).map(function(a){var c=a.y;return u&&!b[a.idx].nonStackable&&(c=a.y>0?a.y1:a.y1+a.y),c}).concat(s))).range(g||[F,0]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]+.01*n.domain()[0],n.domain()[1]-.01*n.domain()[1]]:[-1,1]),h=h||m,i=i||n;var J=p.selectAll("g.nv-wrap.nv-multibar").data([b]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar"),L=K.append("defs"),M=K.append("g"),N=J.select("g");M.append("g").attr("class","nv-groups"),J.attr("transform","translate("+j.left+","+j.top+")"),L.append("clipPath").attr("id","nv-edge-clip-"+o).append("rect"),J.select("#nv-edge-clip-"+o+" rect").attr("width",E).attr("height",F),N.attr("clip-path",t?"url(#nv-edge-clip-"+o+")":"");var O=J.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});O.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);var P=C.transition(O.exit().selectAll("rect.nv-bar"),"multibarExit",Math.min(100,z)).attr("y",function(a){var c=i(0)||0;return u&&b[a.series]&&!b[a.series].nonStackable&&(c=i(a.y0)),c}).attr("height",0).remove();P.delay&&P.delay(function(a,b){var c=b*(z/(D+1))-b;return c}),O.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return w(a,b)}).style("stroke",function(a,b){return w(a,b)}),O.style("stroke-opacity",1).style("fill-opacity",.75);var Q=O.selectAll("rect.nv-bar").data(function(a){return x&&!b.length?x.values:a.values});Q.exit().remove();Q.enter().append("rect").attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("x",function(a,c,d){return u&&!b[d].nonStackable?0:d*m.rangeBand()/b.length}).attr("y",function(a,c,d){return i(u&&!b[d].nonStackable?a.y0:0)||0}).attr("height",0).attr("width",function(a,c,d){return m.rangeBand()/(u&&!b[d].nonStackable?1:b.length)}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"});Q.style("fill",function(a,b,c){return w(a,c,b)}).style("stroke",function(a,b,c){return w(a,c,b)}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),B.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),B.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){B.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){B.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){B.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),Q.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"}),y&&(c||(c=b.map(function(){return!0})),Q.style("fill",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}));var R=Q.watchTransition(C,"multibar",Math.min(250,z)).delay(function(a,c){return c*z/b[0].values.length});u?R.attr("y",function(a,c,d){var e=0;return e=b[d].nonStackable?r(a,c)<0?n(0):n(0)-n(r(a,c))<-1?n(0)-1:n(r(a,c))||0:n(a.y1)}).attr("height",function(a,c,d){return b[d].nonStackable?Math.max(Math.abs(n(r(a,c))-n(0)),1)||0:Math.max(Math.abs(n(a.y+a.y0)-n(a.y0)),1)}).attr("x",function(a,c,d){var e=0;return b[d].nonStackable&&(e=a.series*m.rangeBand()/b.length,b.length!==G&&(e=b[d].nonStackableSeries*m.rangeBand()/(2*G))),e}).attr("width",function(a,c,d){if(b[d].nonStackable){var e=m.rangeBand()/G;return b.length!==G&&(e=m.rangeBand()/(2*G)),e}return m.rangeBand()}):R.attr("x",function(a){return a.series*m.rangeBand()/b.length}).attr("width",m.rangeBand()/b.length).attr("y",function(a,b){return r(a,b)<0?n(0):n(0)-n(r(a,b))<1?n(0)-1:n(r(a,b))||0}).attr("height",function(a,b){return Math.max(Math.abs(n(r(a,b))-n(0)),1)||0}),h=m.copy(),i=n.copy(),b[0]&&b[0].values&&(D=b[0].values.length)}),C.renderEnd("multibar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=d3.scale.ordinal(),n=d3.scale.linear(),o=Math.floor(1e4*Math.random()),p=null,q=function(a){return a.x},r=function(a){return a.y},s=[0],t=!0,u=!1,v="zero",w=a.utils.defaultColor(),x=!1,y=null,z=500,A=.1,B=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),C=a.utils.renderWatch(B,z),D=0;return b.dispatch=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return s},set:function(a){s=a}},stacked:{get:function(){return u},set:function(a){u=a}},stackOffset:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return t},set:function(a){t=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return o},set:function(a){o=a}},hideable:{get:function(){return x},set:function(a){x=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z)}},color:{get:function(){return w},set:function(b){w=a.utils.getColor(b)}},barColor:{get:function(){return y},set:function(b){y=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarChart=function(){"use strict";function b(j){return D.reset(),D.models(e),r&&D.models(f),s&&D.models(g),j.each(function(j){var z=d3.select(this);a.utils.initSVG(z);var D=a.utils.availableWidth(l,z,k),H=a.utils.availableHeight(m,z,k);if(b.update=function(){0===C?z.call(b):z.transition().duration(C).call(b)},b.container=this,x.setter(G(j),b.update).getter(F(j)).update(),x.disabled=j.map(function(a){return!!a.disabled}),!y){var I;y={};for(I in x)y[I]=x[I]instanceof Array?x[I].slice(0):x[I]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,z),b;z.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale(); +var J=z.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([j]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g"),L=J.select("g");if(K.append("g").attr("class","nv-x nv-axis"),K.append("g").attr("class","nv-y nv-axis"),K.append("g").attr("class","nv-barsWrap"),K.append("g").attr("class","nv-legendWrap"),K.append("g").attr("class","nv-controlsWrap"),q&&(h.width(D-B()),L.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),H=a.utils.availableHeight(m,z,k)),L.select(".nv-legendWrap").attr("transform","translate("+B()+","+-k.top+")")),o){var M=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(B()).color(["#444","#444","#444"]),L.select(".nv-controlsWrap").datum(M).attr("transform","translate(0,"+-k.top+")").call(i)}J.attr("transform","translate("+k.left+","+k.top+")"),t&&L.select(".nv-y.nv-axis").attr("transform","translate("+D+",0)"),e.disabled(j.map(function(a){return a.disabled})).width(D).height(H).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var N=L.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(N.call(e),r){f.scale(c)._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-H,0),L.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),L.select(".nv-x.nv-axis").call(f);var O=L.select(".nv-x.nv-axis > g").selectAll("g");if(O.selectAll("line, text").style("opacity",1),v){var P=function(a,b){return"translate("+a+","+b+")"},Q=5,R=17;O.selectAll("text").attr("transform",function(a,b,c){return P(0,c%2==0?Q:R)});var S=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;L.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(a,b){return P(0,0===b||S%2!==0?R:Q)})}u&&O.filter(function(a,b){return b%Math.ceil(j[0].values.length/(D/100))!==0}).selectAll("text, line").style("opacity",0),w&&O.selectAll(".tick text").attr("transform","rotate("+w+" 0,0)").style("text-anchor",w>0?"start":"end"),L.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}s&&(g.scale(d)._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-D,0),L.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(M=M.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":case p.grouped:e.stacked(!1);break;case"Stacked":case p.stacked:e.stacked(!0)}x.stacked=e.stacked(),A.stateChange(x),b.update()}}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),x.stacked=a.stacked,E=a.stacked),b.update()})}),D.renderEnd("multibarchart immediate"),b}var c,d,e=a.models.multiBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=0,x=a.utils.state(),y=null,z=null,A=d3.dispatch("stateChange","changeState","renderEnd"),B=function(){return o?180:0},C=250;x.stacked=!1,e.stacked(!1),f.orient("bottom").tickPadding(7).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(t?"right":"left").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var D=a.utils.renderWatch(A),E=!1,F=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:E}}},G=function(a){return function(b){void 0!==b.stacked&&(E=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=A,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=x,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return y},set:function(a){y=a}},noData:{get:function(){return z},set:function(a){z=a}},reduceXTicks:{get:function(){return u},set:function(a){u=a}},rotateLabels:{get:function(){return w},set:function(a){w=a}},staggerLabels:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return C},set:function(a){C=a,e.duration(C),f.duration(C),g.duration(C),D.reset(C)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiBarHorizontal=function(){"use strict";function b(m){return E.reset(),m.each(function(b){var m=k-j.left-j.right,C=l-j.top-j.bottom;n=d3.select(this),a.utils.initSVG(n),w&&(b=d3.layout.stack().offset("zero").values(function(a){return a.values}).y(r)(b)),b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),w&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e-b.size,e-=b.size):(b.y1=d,d+=b.size)})});var F=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:q(a,b),y:r(a,b),y0:a.y0,y1:a.y1}})});o.domain(d||d3.merge(F).map(function(a){return a.x})).rangeBands(f||[0,C],A),p.domain(e||d3.extent(d3.merge(F).map(function(a){return w?a.y>0?a.y1+a.y:a.y1:a.y}).concat(t))),p.range(x&&!w?g||[p.domain()[0]<0?z:0,m-(p.domain()[1]>0?z:0)]:g||[0,m]),h=h||o,i=i||d3.scale.linear().domain(p.domain()).range([p(0),p(0)]);{var G=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([b]),H=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal"),I=(H.append("defs"),H.append("g"));G.select("g")}I.append("g").attr("class","nv-groups"),G.attr("transform","translate("+j.left+","+j.top+")");var J=G.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});J.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),J.exit().watchTransition(E,"multibarhorizontal: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),J.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return u(a,b)}).style("stroke",function(a,b){return u(a,b)}),J.watchTransition(E,"multibarhorizontal: groups").style("stroke-opacity",1).style("fill-opacity",.75);var K=J.selectAll("g.nv-bar").data(function(a){return a.values});K.exit().remove();var L=K.enter().append("g").attr("transform",function(a,c,d){return"translate("+i(w?a.y0:0)+","+(w?0:d*o.rangeBand()/b.length+o(q(a,c)))+")"});L.append("rect").attr("width",0).attr("height",o.rangeBand()/(w?1:b.length)),K.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),D.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){D.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){D.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){D.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0],0)&&(L.append("polyline"),K.select("polyline").attr("fill","none").attr("points",function(a,c){var d=s(a,c),e=.8*o.rangeBand()/(2*(w?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return p(a)-p(0)});var f=[[d[0],-e],[d[0],e],[d[0],0],[d[1],0],[d[1],-e],[d[1],e]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=o.rangeBand()/(2*(w?1:b.length));return"translate("+(r(a,c)<0?0:p(r(a,c))-p(0))+", "+d+")"})),L.append("text"),x&&!w?(K.select("text").attr("text-anchor",function(a,b){return r(a,b)<0?"end":"start"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){var c=B(r(a,b)),d=s(a,b);return void 0===d?c:d.length?c+"+"+B(Math.abs(d[1]))+"-"+B(Math.abs(d[0])):c+"±"+B(Math.abs(d))}),K.watchTransition(E,"multibarhorizontal: bars").select("text").attr("x",function(a,b){return r(a,b)<0?-4:p(r(a,b))-p(0)+4})):K.selectAll("text").text(""),y&&!w?(L.append("text").classed("nv-bar-label",!0),K.select("text.nv-bar-label").attr("text-anchor",function(a,b){return r(a,b)<0?"start":"end"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return q(a,b)}),K.watchTransition(E,"multibarhorizontal: bars").select("text.nv-bar-label").attr("x",function(a,b){return r(a,b)<0?p(0)-p(r(a,b))+4:-4})):K.selectAll("text.nv-bar-label").text(""),K.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),v&&(c||(c=b.map(function(){return!0})),K.style("fill",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()})),w?K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,b){return"translate("+p(a.y1)+","+o(q(a,b))+")"}).select("rect").attr("width",function(a,b){return Math.abs(p(r(a,b)+a.y0)-p(a.y0))}).attr("height",o.rangeBand()):K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,c){return"translate("+p(r(a,c)<0?r(a,c):0)+","+(a.series*o.rangeBand()/b.length+o(q(a,c)))+")"}).select("rect").attr("height",o.rangeBand()/b.length).attr("width",function(a,b){return Math.max(Math.abs(p(r(a,b))-p(0)),1)}),h=o.copy(),i=p.copy()}),E.renderEnd("multibarHorizontal immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=null,o=d3.scale.ordinal(),p=d3.scale.linear(),q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=a.utils.defaultColor(),v=null,w=!1,x=!1,y=!1,z=60,A=.1,B=d3.format(",.2f"),C=250,D=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),E=a.utils.renderWatch(D,C);return b.dispatch=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return o},set:function(a){o=a}},yScale:{get:function(){return p},set:function(a){p=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return w},set:function(a){w=a}},showValues:{get:function(){return x},set:function(a){x=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return m},set:function(a){m=a}},valueFormat:{get:function(){return B},set:function(a){B=a}},valuePadding:{get:function(){return z},set:function(a){z=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return C},set:function(a){C=a,E.reset(C)}},color:{get:function(){return u},set:function(b){u=a.utils.getColor(b)}},barColor:{get:function(){return v},set:function(b){v=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarHorizontalChart=function(){"use strict";function b(j){return C.reset(),C.models(e),r&&C.models(f),s&&C.models(g),j.each(function(j){var w=d3.select(this);a.utils.initSVG(w);var C=a.utils.availableWidth(l,w,k),D=a.utils.availableHeight(m,w,k);if(b.update=function(){w.transition().duration(z).call(b)},b.container=this,t=e.stacked(),u.setter(B(j),b.update).getter(A(j)).update(),u.disabled=j.map(function(a){return!!a.disabled}),!v){var E;v={};for(E in u)v[E]=u[E]instanceof Array?u[E].slice(0):u[E]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,w),b;w.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var F=w.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([j]),G=F.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g"),H=F.select("g");if(G.append("g").attr("class","nv-x nv-axis"),G.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),G.append("g").attr("class","nv-barsWrap"),G.append("g").attr("class","nv-legendWrap"),G.append("g").attr("class","nv-controlsWrap"),q&&(h.width(C-y()),H.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),D=a.utils.availableHeight(m,w,k)),H.select(".nv-legendWrap").attr("transform","translate("+y()+","+-k.top+")")),o){var I=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(y()).color(["#444","#444","#444"]),H.select(".nv-controlsWrap").datum(I).attr("transform","translate(0,"+-k.top+")").call(i)}F.attr("transform","translate("+k.left+","+k.top+")"),e.disabled(j.map(function(a){return a.disabled})).width(C).height(D).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var J=H.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(J.transition().call(e),r){f.scale(c)._ticks(a.utils.calcTicksY(D/24,j)).tickSize(-C,0),H.select(".nv-x.nv-axis").call(f);var K=H.select(".nv-x.nv-axis").selectAll("g");K.selectAll("line, text")}s&&(g.scale(d)._ticks(a.utils.calcTicksX(C/100,j)).tickSize(-D,0),H.select(".nv-y.nv-axis").attr("transform","translate(0,"+D+")"),H.select(".nv-y.nv-axis").call(g)),H.select(".nv-zeroLine line").attr("x1",d(0)).attr("x2",d(0)).attr("y1",0).attr("y2",-D),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(I=I.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}u.stacked=e.stacked(),x.stateChange(u),t=e.stacked(),b.update()}}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),u.stacked=a.stacked,t=a.stacked),b.update()})}),C.renderEnd("multibar horizontal chart immediate"),b}var c,d,e=a.models.multiBarHorizontal(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend().height(30),i=a.models.legend().height(30),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=a.utils.state(),v=null,w=null,x=d3.dispatch("stateChange","changeState","renderEnd"),y=function(){return o?180:0},z=250;u.stacked=!1,e.stacked(t),f.orient("left").tickPadding(5).showMaxMin(!1).tickFormat(function(a){return a}),g.orient("bottom").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var A=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:t}}},B=function(a){return function(b){void 0!==b.stacked&&(t=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},C=a.utils.renderWatch(x,z);return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=x,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=u,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z),e.duration(z),f.duration(z),g.duration(z)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiChart=function(){"use strict";function b(j){return j.each(function(j){function k(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function l(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.point.x=v.x()(a.point),a.point.y=v.y()(a.point),B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function n(a){var b=2===j[a.data.series].yAxis?z:y;a.value=t.x()(a.data),a.series={value:t.y()(a.data),color:a.color},B.duration(0).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).hidden(!1)}var C=d3.select(this);a.utils.initSVG(C),b.update=function(){C.transition().call(b)},b.container=this;var D=a.utils.availableWidth(g,C,e),E=a.utils.availableHeight(h,C,e),F=j.filter(function(a){return"line"==a.type&&1==a.yAxis}),G=j.filter(function(a){return"line"==a.type&&2==a.yAxis}),H=j.filter(function(a){return"bar"==a.type&&1==a.yAxis}),I=j.filter(function(a){return"bar"==a.type&&2==a.yAxis}),J=j.filter(function(a){return"area"==a.type&&1==a.yAxis}),K=j.filter(function(a){return"area"==a.type&&2==a.yAxis});if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,C),b;C.selectAll(".nv-noData").remove();var L=j.filter(function(a){return!a.disabled&&1==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})}),M=j.filter(function(a){return!a.disabled&&2==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})});o.domain(d3.extent(d3.merge(L.concat(M)),function(a){return a.x})).range([0,D]);var N=C.selectAll("g.wrap.multiChart").data([j]),O=N.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y1 nv-axis"),O.append("g").attr("class","nv-y2 nv-axis"),O.append("g").attr("class","lines1Wrap"),O.append("g").attr("class","lines2Wrap"),O.append("g").attr("class","bars1Wrap"),O.append("g").attr("class","bars2Wrap"),O.append("g").attr("class","stack1Wrap"),O.append("g").attr("class","stack2Wrap"),O.append("g").attr("class","legendWrap");var P=N.select("g"),Q=j.map(function(a,b){return j[b].color||f(a,b)});if(i){var R=A.align()?D/2:D,S=A.align()?R:0;A.width(R),A.color(Q),P.select(".legendWrap").datum(j.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(1==a.yAxis?"":" (right axis)"),a})).call(A),e.top!=A.height()&&(e.top=A.height(),E=a.utils.availableHeight(h,C,e)),P.select(".legendWrap").attr("transform","translate("+S+","+-e.top+")")}r.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"line"==j[b].type})),s.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"line"==j[b].type})),t.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"bar"==j[b].type})),u.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"bar"==j[b].type})),v.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"area"==j[b].type})),w.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"area"==j[b].type})),P.attr("transform","translate("+e.left+","+e.top+")");var T=P.select(".lines1Wrap").datum(F.filter(function(a){return!a.disabled})),U=P.select(".bars1Wrap").datum(H.filter(function(a){return!a.disabled})),V=P.select(".stack1Wrap").datum(J.filter(function(a){return!a.disabled})),W=P.select(".lines2Wrap").datum(G.filter(function(a){return!a.disabled})),X=P.select(".bars2Wrap").datum(I.filter(function(a){return!a.disabled})),Y=P.select(".stack2Wrap").datum(K.filter(function(a){return!a.disabled})),Z=J.length?J.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[],$=K.length?K.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[];p.domain(c||d3.extent(d3.merge(L).concat(Z),function(a){return a.y})).range([0,E]),q.domain(d||d3.extent(d3.merge(M).concat($),function(a){return a.y})).range([0,E]),r.yDomain(p.domain()),t.yDomain(p.domain()),v.yDomain(p.domain()),s.yDomain(q.domain()),u.yDomain(q.domain()),w.yDomain(q.domain()),J.length&&d3.transition(V).call(v),K.length&&d3.transition(Y).call(w),H.length&&d3.transition(U).call(t),I.length&&d3.transition(X).call(u),F.length&&d3.transition(T).call(r),G.length&&d3.transition(W).call(s),x._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-E,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+E+")"),d3.transition(P.select(".nv-x.nv-axis")).call(x),y._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y1.nv-axis")).call(y),z._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y2.nv-axis")).call(z),P.select(".nv-y1.nv-axis").classed("nv-disabled",L.length?!1:!0).attr("transform","translate("+o.range()[0]+",0)"),P.select(".nv-y2.nv-axis").classed("nv-disabled",M.length?!1:!0).attr("transform","translate("+o.range()[1]+",0)"),A.dispatch.on("stateChange",function(){b.update()}),r.dispatch.on("elementMouseover.tooltip",k),s.dispatch.on("elementMouseover.tooltip",k),r.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),s.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),v.dispatch.on("elementMouseover.tooltip",l),w.dispatch.on("elementMouseover.tooltip",l),v.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),w.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMouseover.tooltip",n),u.dispatch.on("elementMouseover.tooltip",n),t.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),u.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()}),u.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()})}),b}var c,d,e={top:30,right:20,bottom:50,left:60},f=a.utils.defaultColor(),g=null,h=null,i=!0,j=null,k=function(a){return a.x},l=function(a){return a.y},m="monotone",n=!0,o=d3.scale.linear(),p=d3.scale.linear(),q=d3.scale.linear(),r=a.models.line().yScale(p),s=a.models.line().yScale(q),t=a.models.multiBar().stacked(!1).yScale(p),u=a.models.multiBar().stacked(!1).yScale(q),v=a.models.stackedArea().yScale(p),w=a.models.stackedArea().yScale(q),x=a.models.axis().scale(o).orient("bottom").tickPadding(5),y=a.models.axis().scale(p).orient("left"),z=a.models.axis().scale(q).orient("right"),A=a.models.legend().height(30),B=a.models.tooltip(),C=d3.dispatch();return b.dispatch=C,b.lines1=r,b.lines2=s,b.bars1=t,b.bars2=u,b.stack1=v,b.stack2=w,b.xAxis=x,b.yAxis1=y,b.yAxis2=z,b.tooltip=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},showLegend:{get:function(){return i},set:function(a){i=a}},yDomain1:{get:function(){return c},set:function(a){c=a}},yDomain2:{get:function(){return d},set:function(a){d=a}},noData:{get:function(){return j},set:function(a){j=a}},interpolate:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return B.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),B.enabled(!!b)}},tooltipContent:{get:function(){return B.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),B.contentGenerator(b)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return f},set:function(b){f=a.utils.getColor(b)}},x:{get:function(){return k},set:function(a){k=a,r.x(a),s.x(a),t.x(a),u.x(a),v.x(a),w.x(a)}},y:{get:function(){return l},set:function(a){l=a,r.y(a),s.y(a),v.y(a),w.y(a),t.y(a),u.y(a)}},useVoronoi:{get:function(){return n},set:function(a){n=a,r.useVoronoi(a),s.useVoronoi(a),v.useVoronoi(a),w.useVoronoi(a)}}}),a.utils.initOptions(b),b},a.models.ohlcBar=function(){"use strict";function b(y){return y.each(function(b){k=d3.select(this);var y=a.utils.availableWidth(h,k,g),A=a.utils.availableHeight(i,k,g);a.utils.initSVG(k);var B=y/b[0].values.length*.9;l.domain(c||d3.extent(b[0].values.map(n).concat(t))),l.range(v?e||[.5*y/b[0].values.length,y*(b[0].values.length-.5)/b[0].values.length]:e||[5+B/2,y-B/2-5]),m.domain(d||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(f||[A,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var C=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([b[0].values]),D=C.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar"),E=D.append("defs"),F=D.append("g"),G=C.select("g");F.append("g").attr("class","nv-ticks"),C.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:j})}),E.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),C.select("#nv-chart-clip-path-"+j+" rect").attr("width",y).attr("height",A),G.attr("clip-path",w?"url(#nv-chart-clip-path-"+j+")":"");var H=C.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});H.exit().remove(),H.enter().append("path").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}).attr("d",function(a,b){return"m0,0l0,"+(m(p(a,b))-m(r(a,b)))+"l"+-B/2+",0l"+B/2+",0l0,"+(m(s(a,b))-m(p(a,b)))+"l0,"+(m(q(a,b))-m(s(a,b)))+"l"+B/2+",0l"+-B/2+",0z"}).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("fill",function(){return x[0]}).attr("stroke",function(){return x[0]}).attr("x",0).attr("y",function(a,b){return m(Math.max(0,o(a,b)))}).attr("height",function(a,b){return Math.abs(m(o(a,b))-m(0))}),H.attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}),d3.transition(H).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("d",function(a,c){var d=y/b[0].values.length*.9;return"m0,0l0,"+(m(p(a,c))-m(r(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(m(s(a,c))-m(p(a,c)))+"l0,"+(m(q(a,c))-m(s(a,c)))+"l"+d/2+",0l"+-d/2+",0z"})}),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,c){b.clearHighlights(),k.select(".nv-ohlcBar .nv-tick-0-"+a).classed("hover",c)},b.clearHighlights=function(){k.select(".nv-ohlcBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!=a.top?a.top:g.top,g.right=void 0!=a.right?a.right:g.right,g.bottom=void 0!=a.bottom?a.bottom:g.bottom,g.left=void 0!=a.left?a.left:g.left +}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.parallelCoordinates=function(){"use strict";function b(p){return p.each(function(b){function p(a){return F(h.map(function(b){if(isNaN(a[b])||isNaN(parseFloat(a[b]))){var c=g[b].domain(),d=g[b].range(),e=c[0]-(c[1]-c[0])/9;if(J.indexOf(b)<0){var h=d3.scale.linear().domain([e,c[1]]).range([x-12,d[1]]);g[b].brush.y(h),J.push(b)}return[f(b),g[b](e)]}return J.length>0?(D.style("display","inline"),E.style("display","inline")):(D.style("display","none"),E.style("display","none")),[f(b),g[b](a[b])]}))}function q(){var a=h.filter(function(a){return!g[a].brush.empty()}),b=a.map(function(a){return g[a].brush.extent()});k=[],a.forEach(function(a,c){k[c]={dimension:a,extent:b[c]}}),l=[],M.style("display",function(c){var d=a.every(function(a,d){return isNaN(c[a])&&b[d][0]==g[a].brush.y().domain()[0]?!0:b[d][0]<=c[a]&&c[a]<=b[d][1]});return d&&l.push(c),d?null:"none"}),o.brush({filters:k,active:l})}function r(a){m[a]=this.parentNode.__origin__=f(a),L.attr("visibility","hidden")}function s(a){m[a]=Math.min(w,Math.max(0,this.parentNode.__origin__+=d3.event.x)),M.attr("d",p),h.sort(function(a,b){return u(a)-u(b)}),f.domain(h),N.attr("transform",function(a){return"translate("+u(a)+")"})}function t(a){delete this.parentNode.__origin__,delete m[a],d3.select(this.parentNode).attr("transform","translate("+f(a)+")"),M.attr("d",p),L.attr("d",p).attr("visibility",null)}function u(a){var b=m[a];return null==b?f(a):b}var v=d3.select(this),w=a.utils.availableWidth(d,v,c),x=a.utils.availableHeight(e,v,c);a.utils.initSVG(v),l=b,f.rangePoints([0,w],1).domain(h);var y={};h.forEach(function(a){var c=d3.extent(b,function(b){return+b[a]});return y[a]=!1,void 0===c[0]&&(y[a]=!0,c[0]=0,c[1]=0),c[0]===c[1]&&(c[0]=c[0]-1,c[1]=c[1]+1),g[a]=d3.scale.linear().domain(c).range([.9*(x-12),0]),g[a].brush=d3.svg.brush().y(g[a]).on("brush",q),"name"!=a});var z=v.selectAll("g.nv-wrap.nv-parallelCoordinates").data([b]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-parallelCoordinates"),B=A.append("g"),C=z.select("g");B.append("g").attr("class","nv-parallelCoordinates background"),B.append("g").attr("class","nv-parallelCoordinates foreground"),B.append("g").attr("class","nv-parallelCoordinates missingValuesline"),z.attr("transform","translate("+c.left+","+c.top+")");var D,E,F=d3.svg.line().interpolate("cardinal").tension(n),G=d3.svg.axis().orient("left"),H=d3.behavior.drag().on("dragstart",r).on("drag",s).on("dragend",t),I=f.range()[1]-f.range()[0],J=[],K=[0+I/2,x-12,w-I/2,x-12];D=z.select(".missingValuesline").selectAll("line").data([K]),D.enter().append("line"),D.exit().remove(),D.attr("x1",function(a){return a[0]}).attr("y1",function(a){return a[1]}).attr("x2",function(a){return a[2]}).attr("y2",function(a){return a[3]}),E=z.select(".missingValuesline").selectAll("text").data(["undefined values"]),E.append("text").data(["undefined values"]),E.enter().append("text"),E.exit().remove(),E.attr("y",x).attr("x",w-92-I/2).text(function(a){return a});var L=z.select(".background").selectAll("path").data(b);L.enter().append("path"),L.exit().remove(),L.attr("d",p);var M=z.select(".foreground").selectAll("path").data(b);M.enter().append("path"),M.exit().remove(),M.attr("d",p).attr("stroke",j),M.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),o.elementMouseover({label:a.name,data:a.data,index:b,pos:[d3.mouse(this.parentNode)[0],d3.mouse(this.parentNode)[1]]})}),M.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),o.elementMouseout({label:a.name,data:a.data,index:b})});var N=C.selectAll(".dimension").data(h),O=N.enter().append("g").attr("class","nv-parallelCoordinates dimension");O.append("g").attr("class","nv-parallelCoordinates nv-axis"),O.append("g").attr("class","nv-parallelCoordinates-brush"),O.append("text").attr("class","nv-parallelCoordinates nv-label"),N.attr("transform",function(a){return"translate("+f(a)+",0)"}),N.exit().remove(),N.select(".nv-label").style("cursor","move").attr("dy","-1em").attr("text-anchor","middle").text(String).on("mouseover",function(a){o.elementMouseover({dim:a,pos:[d3.mouse(this.parentNode.parentNode)[0],d3.mouse(this.parentNode.parentNode)[1]]})}).on("mouseout",function(a){o.elementMouseout({dim:a})}).call(H),N.select(".nv-axis").each(function(a,b){d3.select(this).call(G.scale(g[a]).tickFormat(d3.format(i[b])))}),N.select(".nv-parallelCoordinates-brush").each(function(a){d3.select(this).call(g[a].brush)}).selectAll("rect").attr("x",-8).attr("width",16)}),b}var c={top:30,right:0,bottom:10,left:0},d=null,e=null,f=d3.scale.ordinal(),g={},h=[],i=[],j=a.utils.defaultColor(),k=[],l=[],m=[],n=1,o=d3.dispatch("brush","elementMouseover","elementMouseout");return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},dimensionNames:{get:function(){return h},set:function(a){h=a}},dimensionFormats:{get:function(){return i},set:function(a){i=a}},lineTension:{get:function(){return n},set:function(a){n=a}},dimensions:{get:function(){return h},set:function(b){a.deprecated("dimensions","use dimensionNames instead"),h=b}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.pie=function(){"use strict";function b(E){return D.reset(),E.each(function(b){function E(a,b){a.endAngle=isNaN(a.endAngle)?0:a.endAngle,a.startAngle=isNaN(a.startAngle)?0:a.startAngle,p||(a.innerRadius=0);var c=d3.interpolate(this._current,a);return this._current=c(0),function(a){return B[b](c(a))}}var F=d-c.left-c.right,G=e-c.top-c.bottom,H=Math.min(F,G)/2,I=[],J=[];if(i=d3.select(this),0===z.length)for(var K=H-H/5,L=y*H,M=0;Mc)return"";if("function"==typeof n)d=n(a,b,{key:f(a.data),value:g(a.data),percent:k(c)});else switch(n){case"key":d=f(a.data);break;case"value":d=k(g(a.data));break;case"percent":d=d3.format("%")(c)}return d})}}),D.renderEnd("pie immediate"),b}var c={top:0,right:0,bottom:0,left:0},d=500,e=500,f=function(a){return a.x},g=function(a){return a.y},h=Math.floor(1e4*Math.random()),i=null,j=a.utils.defaultColor(),k=d3.format(",.2f"),l=!0,m=!1,n="key",o=.02,p=!1,q=!1,r=!0,s=0,t=!1,u=!1,v=!1,w=!1,x=0,y=.5,z=[],A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),B=[],C=[],D=a.utils.renderWatch(A);return b.dispatch=A,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{arcsRadius:{get:function(){return z},set:function(a){z=a}},width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},showLabels:{get:function(){return l},set:function(a){l=a}},title:{get:function(){return q},set:function(a){q=a}},titleOffset:{get:function(){return s},set:function(a){s=a}},labelThreshold:{get:function(){return o},set:function(a){o=a}},valueFormat:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return h},set:function(a){h=a}},endAngle:{get:function(){return w},set:function(a){w=a}},startAngle:{get:function(){return u},set:function(a){u=a}},padAngle:{get:function(){return v},set:function(a){v=a}},cornerRadius:{get:function(){return x},set:function(a){x=a}},donutRatio:{get:function(){return y},set:function(a){y=a}},labelsOutside:{get:function(){return m},set:function(a){m=a}},labelSunbeamLayout:{get:function(){return t},set:function(a){t=a}},donut:{get:function(){return p},set:function(a){p=a}},growOnHover:{get:function(){return r},set:function(a){r=a}},pieLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("pieLabelsOutside","use labelsOutside instead")}},donutLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("donutLabelsOutside","use labelsOutside instead")}},labelFormat:{get:function(){return k},set:function(b){k=b,a.deprecated("labelFormat","use valueFormat instead")}},margin:{get:function(){return c},set:function(a){c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left}},y:{get:function(){return g},set:function(a){g=d3.functor(a)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},labelType:{get:function(){return n},set:function(a){n=a||"key"}}}),a.utils.initOptions(b),b},a.models.pieChart=function(){"use strict";function b(e){return q.reset(),q.models(c),e.each(function(e){var k=d3.select(this);a.utils.initSVG(k);var n=a.utils.availableWidth(g,k,f),o=a.utils.availableHeight(h,k,f);if(b.update=function(){k.transition().call(b)},b.container=this,l.setter(s(e),b.update).getter(r(e)).update(),l.disabled=e.map(function(a){return!!a.disabled}),!m){var q;m={};for(q in l)m[q]=l[q]instanceof Array?l[q].slice(0):l[q]}if(!e||!e.length)return a.utils.noData(b,k),b;k.selectAll(".nv-noData").remove();var t=k.selectAll("g.nv-wrap.nv-pieChart").data([e]),u=t.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g"),v=t.select("g");if(u.append("g").attr("class","nv-pieWrap"),u.append("g").attr("class","nv-legendWrap"),i)if("top"===j)d.width(n).key(c.x()),t.select(".nv-legendWrap").datum(e).call(d),f.top!=d.height()&&(f.top=d.height(),o=a.utils.availableHeight(h,k,f)),t.select(".nv-legendWrap").attr("transform","translate(0,"+-f.top+")");else if("right"===j){var w=a.models.legend().width();w>n/2&&(w=n/2),d.height(o).key(c.x()),d.width(w),n-=d.width(),t.select(".nv-legendWrap").datum(e).call(d).attr("transform","translate("+n+",0)")}t.attr("transform","translate("+f.left+","+f.top+")"),c.width(n).height(o);var x=v.select(".nv-pieWrap").datum([e]);d3.transition(x).call(c),d.dispatch.on("stateChange",function(a){for(var c in a)l[c]=a[c];p.stateChange(l),b.update()}),p.on("changeState",function(a){"undefined"!=typeof a.disabled&&(e.forEach(function(b,c){b.disabled=a.disabled[c]}),l.disabled=a.disabled),b.update()})}),q.renderEnd("pieChart immediate"),b}var c=a.models.pie(),d=a.models.legend(),e=a.models.tooltip(),f={top:30,right:20,bottom:20,left:20},g=null,h=null,i=!0,j="top",k=a.utils.defaultColor(),l=a.utils.state(),m=null,n=null,o=250,p=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd");e.headerEnabled(!1).duration(0).valueFormatter(function(a,b){return c.valueFormat()(a,b)});var q=a.utils.renderWatch(p),r=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},s=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},e.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){e.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){e.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.legend=d,b.dispatch=p,b.pie=c,b.tooltip=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return i},set:function(a){i=a}},legendPosition:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return e.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),e.enabled(!!b)}},tooltipContent:{get:function(){return e.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),e.contentGenerator(b)}},color:{get:function(){return k},set:function(a){k=a,d.color(k),c.color(k)}},duration:{get:function(){return o},set:function(a){o=a,q.reset(o)}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.scatter=function(){"use strict";function b(N){return P.reset(),N.each(function(b){function N(){if(O=!1,!w)return!1;if(M===!0){var a=d3.merge(b.map(function(a,b){return a.values.map(function(a,c){var d=p(a,c),e=q(a,c);return[m(d)+1e-4*Math.random(),n(e)+1e-4*Math.random(),b,c,a]}).filter(function(a,b){return x(a[4],b)})}));if(0==a.length)return!1;a.length<3&&(a.push([m.range()[0]-20,n.range()[0]-20,null,null]),a.push([m.range()[1]+20,n.range()[1]+20,null,null]),a.push([m.range()[0]-20,n.range()[0]+20,null,null]),a.push([m.range()[1]+20,n.range()[1]-20,null,null]));var c=d3.geom.polygon([[-10,-10],[-10,i+10],[h+10,i+10],[h+10,-10]]),d=d3.geom.voronoi(a).map(function(b,d){return{data:c.clip(b),series:a[d][2],point:a[d][3]}});U.select(".nv-point-paths").selectAll("path").remove();var e=U.select(".nv-point-paths").selectAll("path").data(d),f=e.enter().append("svg:path").attr("d",function(a){return a&&a.data&&0!==a.data.length?"M"+a.data.join(",")+"Z":"M 0 0"}).attr("id",function(a,b){return"nv-path-"+b}).attr("clip-path",function(a,b){return"url(#nv-clip-"+b+")"});C&&f.style("fill",d3.rgb(230,230,230)).style("fill-opacity",.4).style("stroke-opacity",1).style("stroke",d3.rgb(200,200,200)),B&&(U.select(".nv-point-clips").selectAll("clipPath").remove(),U.select(".nv-point-clips").selectAll("clipPath").data(a).enter().append("svg:clipPath").attr("id",function(a,b){return"nv-clip-"+b}).append("svg:circle").attr("cx",function(a){return a[0]}).attr("cy",function(a){return a[1]}).attr("r",D));var k=function(a,c){if(O)return 0;var d=b[a.series];if(void 0!==d){var e=d.values[a.point];e.color=j(d,a.series),e.x=p(e),e.y=q(e);var f=l.node().getBoundingClientRect(),h=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,k={left:m(p(e,a.point))+f.left+i+g.left+10,top:n(q(e,a.point))+f.top+h+g.top+10};c({point:e,series:d,pos:k,seriesIndex:a.series,pointIndex:a.point})}};e.on("click",function(a){k(a,L.elementClick)}).on("dblclick",function(a){k(a,L.elementDblClick)}).on("mouseover",function(a){k(a,L.elementMouseover)}).on("mouseout",function(a){k(a,L.elementMouseout)})}else U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("dblclick",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementDblClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseover",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseover({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c,color:j(a,c)})}).on("mouseout",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseout({point:e,series:d,seriesIndex:a.series,pointIndex:c,color:j(a,c)})})}l=d3.select(this);var R=a.utils.availableWidth(h,l,g),S=a.utils.availableHeight(i,l,g);a.utils.initSVG(l),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var T=E&&F&&I?[]:d3.merge(b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),size:r(a,b)}})}));m.domain(E||d3.extent(T.map(function(a){return a.x}).concat(t))),m.range(y&&b[0]?G||[(R*z+R)/(2*b[0].values.length),R-R*(1+z)/(2*b[0].values.length)]:G||[0,R]),n.domain(F||d3.extent(T.map(function(a){return a.y}).concat(u))).range(H||[S,0]),o.domain(I||d3.extent(T.map(function(a){return a.size}).concat(v))).range(J||Q),K=m.domain()[0]===m.domain()[1]||n.domain()[0]===n.domain()[1],m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]-.01*n.domain()[0],n.domain()[1]+.01*n.domain()[1]]:[-1,1]),isNaN(m.domain()[0])&&m.domain([-1,1]),isNaN(n.domain()[0])&&n.domain([-1,1]),c=c||m,d=d||n,e=e||o;var U=l.selectAll("g.nv-wrap.nv-scatter").data([b]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+k),W=V.append("defs"),X=V.append("g"),Y=U.select("g");U.classed("nv-single-point",K),X.append("g").attr("class","nv-groups"),X.append("g").attr("class","nv-point-paths"),V.append("g").attr("class","nv-point-clips"),U.attr("transform","translate("+g.left+","+g.top+")"),W.append("clipPath").attr("id","nv-edge-clip-"+k).append("rect"),U.select("#nv-edge-clip-"+k+" rect").attr("width",R).attr("height",S>0?S:0),Y.attr("clip-path",A?"url(#nv-edge-clip-"+k+")":""),O=!0;var Z=U.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});Z.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),Z.exit().remove(),Z.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),Z.watchTransition(P,"scatter: groups").style("fill",function(a,b){return j(a,b)}).style("stroke",function(a,b){return j(a,b)}).style("stroke-opacity",1).style("fill-opacity",.5);var $=Z.selectAll("path.nv-point").data(function(a){return a.values.map(function(a,b){return[a,b]}).filter(function(a,b){return x(a[0],b)})});$.enter().append("path").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("transform",function(a){return"translate("+c(p(a[0],a[1]))+","+d(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).remove(),$.each(function(a){d3.select(this).classed("nv-point",!0).classed("nv-point-"+a[1],!0).classed("nv-noninteractive",!w).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),clearTimeout(f),f=setTimeout(N,300),c=m.copy(),d=n.copy(),e=o.copy()}),P.renderEnd("scatter immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=a.utils.defaultColor(),k=Math.floor(1e5*Math.random()),l=null,m=d3.scale.linear(),n=d3.scale.linear(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=function(a){return a.size||1},s=function(a){return a.shape||"circle"},t=[],u=[],v=[],w=!0,x=function(a){return!a.notActive},y=!1,z=.1,A=!1,B=!0,C=!1,D=function(){return 25},E=null,F=null,G=null,H=null,I=null,J=null,K=!1,L=d3.dispatch("elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),M=!0,N=250,O=!1,P=a.utils.renderWatch(L,N),Q=[16,256];return b.dispatch=L,b.options=a.utils.optionsFunc.bind(b),b._calls=new function(){this.clearHighlights=function(){return a.dom.write(function(){l.selectAll(".nv-point.hover").classed("hover",!1)}),null},this.highlightPoint=function(b,c,d){a.dom.write(function(){l.select(" .nv-series-"+b+" .nv-point-"+c).classed("hover",d)})}},L.on("elementMouseover.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!0)}),L.on("elementMouseout.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!1)}),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},pointScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return E},set:function(a){E=a}},yDomain:{get:function(){return F},set:function(a){F=a}},pointDomain:{get:function(){return I},set:function(a){I=a}},xRange:{get:function(){return G},set:function(a){G=a}},yRange:{get:function(){return H},set:function(a){H=a}},pointRange:{get:function(){return J},set:function(a){J=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},forcePoint:{get:function(){return v},set:function(a){v=a}},interactive:{get:function(){return w},set:function(a){w=a}},pointActive:{get:function(){return x},set:function(a){x=a}},padDataOuter:{get:function(){return z},set:function(a){z=a}},padData:{get:function(){return y},set:function(a){y=a}},clipEdge:{get:function(){return A},set:function(a){A=a}},clipVoronoi:{get:function(){return B},set:function(a){B=a}},clipRadius:{get:function(){return D},set:function(a){D=a}},showVoronoi:{get:function(){return C},set:function(a){C=a}},id:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return p},set:function(a){p=d3.functor(a)}},y:{get:function(){return q},set:function(a){q=d3.functor(a)}},pointSize:{get:function(){return r},set:function(a){r=d3.functor(a)}},pointShape:{get:function(){return s},set:function(a){s=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},duration:{get:function(){return N},set:function(a){N=a,P.reset(N)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},useVoronoi:{get:function(){return M},set:function(a){M=a,M===!1&&(B=!1)}}}),a.utils.initOptions(b),b},a.models.scatterChart=function(){"use strict";function b(z){return D.reset(),D.models(c),t&&D.models(d),u&&D.models(e),q&&D.models(g),r&&D.models(h),z.each(function(z){m=d3.select(this),a.utils.initSVG(m);var G=a.utils.availableWidth(k,m,j),H=a.utils.availableHeight(l,m,j);if(b.update=function(){0===A?m.call(b):m.transition().duration(A).call(b)},b.container=this,w.setter(F(z),b.update).getter(E(z)).update(),w.disabled=z.map(function(a){return!!a.disabled}),!x){var I;x={};for(I in w)x[I]=w[I]instanceof Array?w[I].slice(0):w[I]}if(!(z&&z.length&&z.filter(function(a){return a.values.length}).length))return a.utils.noData(b,m),D.renderEnd("scatter immediate"),b;m.selectAll(".nv-noData").remove(),o=c.xScale(),p=c.yScale();var J=m.selectAll("g.nv-wrap.nv-scatterChart").data([z]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),L=K.append("g"),M=J.select("g");if(L.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none"),L.append("g").attr("class","nv-x nv-axis"),L.append("g").attr("class","nv-y nv-axis"),L.append("g").attr("class","nv-scatterWrap"),L.append("g").attr("class","nv-regressionLinesWrap"),L.append("g").attr("class","nv-distWrap"),L.append("g").attr("class","nv-legendWrap"),v&&M.select(".nv-y.nv-axis").attr("transform","translate("+G+",0)"),s){var N=G;f.width(N),J.select(".nv-legendWrap").datum(z).call(f),j.top!=f.height()&&(j.top=f.height(),H=a.utils.availableHeight(l,m,j)),J.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")}J.attr("transform","translate("+j.left+","+j.top+")"),c.width(G).height(H).color(z.map(function(a,b){return a.color=a.color||n(a,b),a.color}).filter(function(a,b){return!z[b].disabled})),J.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),J.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+c.id()+")");var O=J.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(a){return a});O.enter().append("g").attr("class","nv-regLines");var P=O.selectAll(".nv-regLine").data(function(a){return[a]});P.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0),P.filter(function(a){return a.intercept&&a.slope}).watchTransition(D,"scatterPlusLineChart: regline").attr("x1",o.range()[0]).attr("x2",o.range()[1]).attr("y1",function(a){return p(o.domain()[0]*a.slope+a.intercept)}).attr("y2",function(a){return p(o.domain()[1]*a.slope+a.intercept)}).style("stroke",function(a,b,c){return n(a,c)}).style("stroke-opacity",function(a){return a.disabled||"undefined"==typeof a.slope||"undefined"==typeof a.intercept?0:1}),t&&(d.scale(o)._ticks(a.utils.calcTicksX(G/100,z)).tickSize(-H,0),M.select(".nv-x.nv-axis").attr("transform","translate(0,"+p.range()[0]+")").call(d)),u&&(e.scale(p)._ticks(a.utils.calcTicksY(H/36,z)).tickSize(-G,0),M.select(".nv-y.nv-axis").call(e)),q&&(g.getData(c.x()).scale(o).width(G).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),M.select(".nv-distributionX").attr("transform","translate(0,"+p.range()[0]+")").datum(z.filter(function(a){return!a.disabled})).call(g)),r&&(h.getData(c.y()).scale(p).width(H).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),M.select(".nv-distributionY").attr("transform","translate("+(v?G:-h.size())+",0)").datum(z.filter(function(a){return!a.disabled})).call(h)),f.dispatch.on("stateChange",function(a){for(var c in a)w[c]=a[c];y.stateChange(w),b.update()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&(z.forEach(function(b,c){b.disabled=a.disabled[c]}),w.disabled=a.disabled),b.update()}),c.dispatch.on("elementMouseout.tooltip",function(a){i.hidden(!0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",h.size())}),c.dispatch.on("elementMouseover.tooltip",function(a){m.select(".nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",a.pos.top-H-j.top),m.select(".nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos.left+g.size()-j.left),i.position(a.pos).data(a).hidden(!1)}),B=o.copy(),C=p.copy()}),D.renderEnd("scatter with line immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.distribution(),h=a.models.distribution(),i=a.models.tooltip(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=null,n=a.utils.defaultColor(),o=c.xScale(),p=c.yScale(),q=!1,r=!1,s=!0,t=!0,u=!0,v=!1,w=a.utils.state(),x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=null,A=250;c.xScale(o).yScale(p),d.orient("bottom").tickPadding(10),e.orient(v?"right":"left").tickPadding(10),g.axis("x"),h.axis("y"),i.headerFormatter(function(a,b){return d.tickFormat()(a,b)}).valueFormatter(function(a,b){return e.tickFormat()(a,b)});var B,C,D=a.utils.renderWatch(y,A),E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return b.dispatch=y,b.scatter=c,b.legend=f,b.xAxis=d,b.yAxis=e,b.distX=g,b.distY=h,b.tooltip=i,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},container:{get:function(){return m},set:function(a){m=a}},showDistX:{get:function(){return q},set:function(a){q=a}},showDistY:{get:function(){return r},set:function(a){r=a}},showLegend:{get:function(){return s},set:function(a){s=a}},showXAxis:{get:function(){return t},set:function(a){t=a}},showYAxis:{get:function(){return u},set:function(a){u=a}},defaultState:{get:function(){return x},set:function(a){x=a}},noData:{get:function(){return z},set:function(a){z=a}},duration:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return i.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),i.enabled(!!b) +}},tooltipContent:{get:function(){return i.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),i.contentGenerator(b)}},tooltipXContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},tooltipYContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},rightAlignYAxis:{get:function(){return v},set:function(a){v=a,e.orient(a?"right":"left")}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),f.color(n),g.color(n),h.color(n)}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.sparkline=function(){"use strict";function b(k){return k.each(function(b){var k=h-g.left-g.right,q=i-g.top-g.bottom;j=d3.select(this),a.utils.initSVG(j),l.domain(c||d3.extent(b,n)).range(e||[0,k]),m.domain(d||d3.extent(b,o)).range(f||[q,0]);{var r=j.selectAll("g.nv-wrap.nv-sparkline").data([b]),s=r.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");s.append("g"),r.select("g")}r.attr("transform","translate("+g.left+","+g.top+")");var t=r.selectAll("path").data(function(a){return[a]});t.enter().append("path"),t.exit().remove(),t.style("stroke",function(a,b){return a.color||p(a,b)}).attr("d",d3.svg.line().x(function(a,b){return l(n(a,b))}).y(function(a,b){return m(o(a,b))}));var u=r.selectAll("circle.nv-point").data(function(a){function b(b){if(-1!=b){var c=a[b];return c.pointIndex=b,c}return null}var c=a.map(function(a,b){return o(a,b)}),d=b(c.lastIndexOf(m.domain()[1])),e=b(c.indexOf(m.domain()[0])),f=b(c.length-1);return[e,d,f].filter(function(a){return null!=a})});u.enter().append("circle"),u.exit().remove(),u.attr("cx",function(a){return l(n(a,a.pointIndex))}).attr("cy",function(a){return m(o(a,a.pointIndex))}).attr("r",2).attr("class",function(a){return n(a,a.pointIndex)==l.domain()[1]?"nv-point nv-currentValue":o(a,a.pointIndex)==m.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})}),b}var c,d,e,f,g={top:2,right:0,bottom:2,left:0},h=400,i=32,j=null,k=!0,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=a.utils.getColor(["#000"]);return b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},animate:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return n},set:function(a){n=d3.functor(a)}},y:{get:function(){return o},set:function(a){o=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return p},set:function(b){p=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sparklinePlus=function(){"use strict";function b(p){return p.each(function(p){function q(){if(!j){var a=z.selectAll(".nv-hoverValue").data(i),b=a.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);a.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove(),a.attr("transform",function(a){return"translate("+c(e.x()(p[a],a))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1),i.length&&(b.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",u),b.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em"),z.select(".nv-hoverValue .nv-xValue").text(k(e.x()(p[i[0]],i[0]))),b.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em"),z.select(".nv-hoverValue .nv-yValue").text(l(e.y()(p[i[0]],i[0]))))}}function r(){function a(a,b){for(var c=Math.abs(e.x()(a[0],0)-b),d=0,f=0;fc;++c){for(b=0,d=0;bb;b++)a[b][c][1]/=d;else for(b=0;e>b;b++)a[b][c][1]=0}for(c=0;f>c;++c)g[c]=0;return g}}),u.renderEnd("stackedArea immediate"),b}var c,d,e={top:0,right:0,bottom:0,left:0},f=960,g=500,h=a.utils.defaultColor(),i=Math.floor(1e5*Math.random()),j=null,k=function(a){return a.x},l=function(a){return a.y},m="stack",n="zero",o="default",p="linear",q=!1,r=a.models.scatter(),s=250,t=d3.dispatch("areaClick","areaMouseover","areaMouseout","renderEnd","elementClick","elementMouseover","elementMouseout");r.pointSize(2.2).pointDomain([2.2,2.2]);var u=a.utils.renderWatch(t,s);return b.dispatch=t,b.scatter=r,r.dispatch.on("elementClick",function(){t.elementClick.apply(this,arguments)}),r.dispatch.on("elementMouseover",function(){t.elementMouseover.apply(this,arguments)}),r.dispatch.on("elementMouseout",function(){t.elementMouseout.apply(this,arguments)}),b.interpolate=function(a){return arguments.length?(p=a,b):p},b.duration=function(a){return arguments.length?(s=a,u.reset(s),r.duration(s),b):s},b.dispatch=t,b.scatter=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return f},set:function(a){f=a}},height:{get:function(){return g},set:function(a){g=a}},clipEdge:{get:function(){return q},set:function(a){q=a}},offset:{get:function(){return n},set:function(a){n=a}},order:{get:function(){return o},set:function(a){o=a}},interpolate:{get:function(){return p},set:function(a){p=a}},x:{get:function(){return k},set:function(a){k=d3.functor(a)}},y:{get:function(){return l},set:function(a){l=d3.functor(a)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return h},set:function(b){h=a.utils.getColor(b)}},style:{get:function(){return m},set:function(a){switch(m=a){case"stack":b.offset("zero"),b.order("default");break;case"stream":b.offset("wiggle"),b.order("inside-out");break;case"stream-center":b.offset("silhouette"),b.order("inside-out");break;case"expand":b.offset("expand"),b.order("default");break;case"stack_percent":b.offset(b.d3_stackedOffset_stackPercent),b.order("default")}}},duration:{get:function(){return s},set:function(a){s=a,u.reset(s),r.duration(s)}}}),a.utils.inheritOptions(b,r),a.utils.initOptions(b),b},a.models.stackedAreaChart=function(){"use strict";function b(k){return F.reset(),F.models(e),r&&F.models(f),s&&F.models(g),k.each(function(k){var x=d3.select(this),F=this;a.utils.initSVG(x);var K=a.utils.availableWidth(m,x,l),L=a.utils.availableHeight(n,x,l);if(b.update=function(){x.transition().duration(C).call(b)},b.container=this,v.setter(I(k),b.update).getter(H(k)).update(),v.disabled=k.map(function(a){return!!a.disabled}),!w){var M;w={};for(M in v)w[M]=v[M]instanceof Array?v[M].slice(0):v[M]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(b,x),b;x.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var N=x.selectAll("g.nv-wrap.nv-stackedAreaChart").data([k]),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g"),P=N.select("g");if(O.append("rect").style("opacity",0),O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y nv-axis"),O.append("g").attr("class","nv-stackedWrap"),O.append("g").attr("class","nv-legendWrap"),O.append("g").attr("class","nv-controlsWrap"),O.append("g").attr("class","nv-interactive"),P.select("rect").attr("width",K).attr("height",L),q){var Q=p?K-z:K;h.width(Q),P.select(".nv-legendWrap").datum(k).call(h),l.top!=h.height()&&(l.top=h.height(),L=a.utils.availableHeight(n,x,l)),P.select(".nv-legendWrap").attr("transform","translate("+(K-Q)+","+-l.top+")")}if(p){var R=[{key:B.stacked||"Stacked",metaKey:"Stacked",disabled:"stack"!=e.style(),style:"stack"},{key:B.stream||"Stream",metaKey:"Stream",disabled:"stream"!=e.style(),style:"stream"},{key:B.expanded||"Expanded",metaKey:"Expanded",disabled:"expand"!=e.style(),style:"expand"},{key:B.stack_percent||"Stack %",metaKey:"Stack_Percent",disabled:"stack_percent"!=e.style(),style:"stack_percent"}];z=A.length/3*260,R=R.filter(function(a){return-1!==A.indexOf(a.metaKey)}),i.width(z).color(["#444","#444","#444"]),P.select(".nv-controlsWrap").datum(R).call(i),l.top!=Math.max(i.height(),h.height())&&(l.top=Math.max(i.height(),h.height()),L=a.utils.availableHeight(n,x,l)),P.select(".nv-controlsWrap").attr("transform","translate(0,"+-l.top+")")}N.attr("transform","translate("+l.left+","+l.top+")"),t&&P.select(".nv-y.nv-axis").attr("transform","translate("+K+",0)"),u&&(j.width(K).height(L).margin({left:l.left,top:l.top}).svgContainer(x).xScale(c),N.select(".nv-interactive").call(j)),e.width(K).height(L);var S=P.select(".nv-stackedWrap").datum(k);if(S.transition().call(e),r&&(f.scale(c)._ticks(a.utils.calcTicksX(K/100,k)).tickSize(-L,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+L+")"),P.select(".nv-x.nv-axis").transition().duration(0).call(f)),s){var T;if(T="wiggle"===e.offset()?0:a.utils.calcTicksY(L/36,k),g.scale(d)._ticks(T).tickSize(-K,0),"expand"===e.style()||"stack_percent"===e.style()){var U=g.tickFormat();D&&U===J||(D=U),g.tickFormat(J)}else D&&(g.tickFormat(D),D=null);P.select(".nv-y.nv-axis").transition().duration(0).call(g)}e.dispatch.on("areaClick.toggle",function(a){k.forEach(1===k.filter(function(a){return!a.disabled}).length?function(a){a.disabled=!1}:function(b,c){b.disabled=c!=a.seriesIndex}),v.disabled=k.map(function(a){return!!a.disabled}),y.stateChange(v),b.update()}),h.dispatch.on("stateChange",function(a){for(var c in a)v[c]=a[c];y.stateChange(v),b.update()}),i.dispatch.on("legendClick",function(a){a.disabled&&(R=R.map(function(a){return a.disabled=!0,a}),a.disabled=!1,e.style(a.style),v.style=e.style(),y.stateChange(v),b.update())}),j.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,g,h,i=[];if(k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,j){g=a.interactiveBisect(f.values,c.pointXValue,b.x());var k=f.values[g],l=b.y()(k,g);if(null!=l&&e.highlightPoint(j,g,!0),"undefined"!=typeof k){"undefined"==typeof d&&(d=k),"undefined"==typeof h&&(h=b.xScale()(b.x()(k,g)));var m="expand"==e.style()?k.display.y:b.y()(k,g);i.push({key:f.key,value:m,color:o(f,f.seriesIndex),stackedValue:k.display})}}),i.reverse(),i.length>2){var m=b.yScale().invert(c.mouseY),n=null;i.forEach(function(a,b){m=Math.abs(m);var c=Math.abs(a.stackedValue.y0),d=Math.abs(a.stackedValue.y);return m>=c&&d+c>=m?void(n=b):void 0}),null!=n&&(i[n].highlight=!0)}var p=f.tickFormat()(b.x()(d,g)),q=j.tooltip.valueFormatter();"expand"===e.style()||"stack_percent"===e.style()?(E||(E=q),q=d3.format(".1%")):E&&(q=E,E=null),j.tooltip.position({left:h+l.left,top:c.mouseY+l.top}).chartContainer(F.parentNode).valueFormatter(q).data({value:p,series:i})(),j.renderGuideLine(h)}),j.dispatch.on("elementMouseout",function(){e.clearHighlights()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&k.length===a.disabled.length&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),v.disabled=a.disabled),"undefined"!=typeof a.style&&(e.style(a.style),G=a.style),b.update()})}),F.renderEnd("stacked Area chart immediate"),b}var c,d,e=a.models.stackedArea(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:25,bottom:50,left:60},m=null,n=null,o=a.utils.defaultColor(),p=!0,q=!0,r=!0,s=!0,t=!1,u=!1,v=a.utils.state(),w=null,x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=250,A=["Stacked","Stream","Expanded"],B={},C=250;v.style=e.style(),f.orient("bottom").tickPadding(7),g.orient(t?"right":"left"),k.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)}),j.tooltip.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)});var D=null,E=null;i.updateState(!1);var F=a.utils.renderWatch(y),G=e.style(),H=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),style:e.style()}}},I=function(a){return function(b){void 0!==b.style&&(G=b.style),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},J=d3.format("%");return e.dispatch.on("elementMouseover.tooltip",function(a){a.point.x=e.x()(a.point),a.point.y=e.y()(a.point),k.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),b.dispatch=y,b.stacked=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.interactiveLayer=j,b.tooltip=k,b.dispatch=y,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return w},set:function(a){w=a}},noData:{get:function(){return x},set:function(a){x=a}},showControls:{get:function(){return p},set:function(a){p=a}},controlLabels:{get:function(){return B},set:function(a){B=a}},controlOptions:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},duration:{get:function(){return C},set:function(a){C=a,F.reset(C),e.duration(C),f.duration(C),g.duration(C)}},color:{get:function(){return o},set:function(b){o=a.utils.getColor(b),h.color(o),e.color(o)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},useInteractiveGuideline:{get:function(){return u},set:function(a){u=!!a,b.interactive(!a),b.useVoronoi(!a),e.scatter.interactive(!a)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.sunburst=function(){"use strict";function b(u){return t.reset(),u.each(function(b){function t(a){a.x0=a.x,a.dx0=a.dx}function u(a){var b=d3.interpolate(p.domain(),[a.x,a.x+a.dx]),c=d3.interpolate(q.domain(),[a.y,1]),d=d3.interpolate(q.range(),[a.y?20:0,y]);return function(a,e){return e?function(){return s(a)}:function(e){return p.domain(b(e)),q.domain(c(e)).range(d(e)),s(a)}}}l=d3.select(this);var v,w=a.utils.availableWidth(g,l,f),x=a.utils.availableHeight(h,l,f),y=Math.min(w,x)/2;a.utils.initSVG(l);var z=l.selectAll(".nv-wrap.nv-sunburst").data(b),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburst nv-chart-"+k),B=A.selectAll("nv-sunburst");z.attr("transform","translate("+w/2+","+x/2+")"),l.on("click",function(a,b){o.chartClick({data:a,index:b,pos:d3.event,id:k})}),q.range([0,y]),c=c||b,e=b[0],r.value(j[i]||j.count),v=B.data(r.nodes).enter().append("path").attr("d",s).style("fill",function(a){return m((a.children?a:a.parent).name)}).style("stroke","#FFF").on("click",function(a){d!==c&&c!==a&&(d=c),c=a,v.transition().duration(n).attrTween("d",u(a))}).each(t).on("dblclick",function(a){d.parent==a&&v.transition().duration(n).attrTween("d",u(e))}).each(t).on("mouseover",function(a){d3.select(this).classed("hover",!0).style("opacity",.8),o.elementMouseover({data:a,color:d3.select(this).style("fill")})}).on("mouseout",function(a){d3.select(this).classed("hover",!1).style("opacity",1),o.elementMouseout({data:a})}).on("mousemove",function(a){o.elementMousemove({data:a})})}),t.renderEnd("sunburst immediate"),b}var c,d,e,f={top:0,right:0,bottom:0,left:0},g=null,h=null,i="count",j={count:function(){return 1},size:function(a){return a.size}},k=Math.floor(1e4*Math.random()),l=null,m=a.utils.defaultColor(),n=500,o=d3.dispatch("chartClick","elementClick","elementDblClick","elementMousemove","elementMouseover","elementMouseout","renderEnd"),p=d3.scale.linear().range([0,2*Math.PI]),q=d3.scale.sqrt(),r=d3.layout.partition().sort(null).value(function(){return 1}),s=d3.svg.arc().startAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x)))}).endAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x+a.dx)))}).innerRadius(function(a){return Math.max(0,q(a.y))}).outerRadius(function(a){return Math.max(0,q(a.y+a.dy))}),t=a.utils.renderWatch(o);return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},mode:{get:function(){return i},set:function(a){i=a}},id:{get:function(){return k},set:function(a){k=a}},duration:{get:function(){return n},set:function(a){n=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!=a.top?a.top:f.top,f.right=void 0!=a.right?a.right:f.right,f.bottom=void 0!=a.bottom?a.bottom:f.bottom,f.left=void 0!=a.left?a.left:f.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sunburstChart=function(){"use strict";function b(d){return m.reset(),m.models(c),d.each(function(d){var h=d3.select(this);a.utils.initSVG(h);var i=a.utils.availableWidth(f,h,e),j=a.utils.availableHeight(g,h,e);if(b.update=function(){0===k?h.call(b):h.transition().duration(k).call(b)},b.container=this,!d||!d.length)return a.utils.noData(b,h),b;h.selectAll(".nv-noData").remove();var l=h.selectAll("g.nv-wrap.nv-sunburstChart").data(d),m=l.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburstChart").append("g"),n=l.select("g");m.append("g").attr("class","nv-sunburstWrap"),l.attr("transform","translate("+e.left+","+e.top+")"),c.width(i).height(j);var o=n.select(".nv-sunburstWrap").datum(d);d3.transition(o).call(c)}),m.renderEnd("sunburstChart immediate"),b}var c=a.models.sunburst(),d=a.models.tooltip(),e={top:30,right:20,bottom:20,left:20},f=null,g=null,h=a.utils.defaultColor(),i=(Math.round(1e5*Math.random()),null),j=null,k=250,l=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),m=a.utils.renderWatch(l);return d.headerEnabled(!1).duration(0).valueFormatter(function(a){return a}),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.data.name,value:a.data.size,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=l,b.sunburst=c,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return i},set:function(a){i=a}},color:{get:function(){return h},set:function(a){h=a,c.color(h)}},duration:{get:function(){return k},set:function(a){k=a,m.reset(k),c.duration(k)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.version="1.8.1"}(); \ No newline at end of file diff --git a/tests/coverage/_js/popper.min.js b/tests/coverage/_js/popper.min.js new file mode 100644 index 0000000..36c2aeb --- /dev/null +++ b/tests/coverage/_js/popper.min.js @@ -0,0 +1,5 @@ +/* + Copyright (C) Federico Zivolo 2019 + Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). + */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?pe:10===e?se:pe||se}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),le({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=fe({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f],10),E=parseFloat(w['border'+f+'Width'],10),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},le(n,m,$(v)),le(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ge.FLIP:p=[n,i];break;case ge.CLOCKWISE:p=G(n);break;case ge.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=fe({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.rightwindow.devicePixelRatio||!me),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=fe({},E,e.attributes),e.styles=fe({},m,e.styles),e.arrowStyles=fe({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ue}); +//# sourceMappingURL=popper.min.js.map diff --git a/tests/coverage/app/Console/Commands/DeleteUnconfirmedCustomers.php.html b/tests/coverage/app/Console/Commands/DeleteUnconfirmedCustomers.php.html new file mode 100644 index 0000000..7170e8e --- /dev/null +++ b/tests/coverage/app/Console/Commands/DeleteUnconfirmedCustomers.php.html @@ -0,0 +1,169 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Console/Commands/DeleteUnconfirmedCustomers.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
DeleteUnconfirmedCustomers
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 handle
n/a
0 / 0
3
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use App\Models\Customer;
6use App\Models\CustomerLogin;
7use Carbon\Carbon;
8use Illuminate\Console\Command;
9
10/**
11 * @codeCoverageIgnore
12 */
13class DeleteUnconfirmedCustomers extends Command
14{
15    /**
16     * The name and signature of the console command.
17     *
18     * @var string
19     */
20    protected $signature = 'customer:deleteuc';
21
22    /**
23     * The console command description.
24     *
25     * @var string
26     */
27    protected $description = 'Delete the customers that haven\'t
28    confirmed their acocunt within 24 hours after the registeration.';
29
30    /**
31     * Execute the console command.
32     *
33     * @return integer
34     */
35    public function handle()
36    {
37        $customersLogins = CustomerLogin::where([['status', 0], ['created_at', '<', Carbon::now()->subDay(1)]])->get();
38        foreach ($customersLogins as $customerLogin) {
39            // Si non activé et créer il y a plus d'un jour : Suppression.
40            $customerLogin->customer->infos()->where('brand_id', $customerLogin->brand_id)->first()->delete();
41            // On récupère son id pour le cas ou on doit le supprimer complètement après.
42            $customerId = $customerLogin->customer->id;
43            // Suppression de ses infos de login pour la marque.
44            $customerLogin->delete();
45            // Si le customer n'a plus d'info il n'a donc plus besoin de compte en crm.
46            if (count($customerLogin->customer->infos) === 0) {
47                // Suppression du customer une fois tout le reste supprimé.
48                $customer = Customer::find($customerId);
49                $customer->optInHistory()->delete();
50                $customer->delete();
51            }
52        }
53        return 0;
54    }
55}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Console/Commands/GenerateCrmClient.php.html b/tests/coverage/app/Console/Commands/GenerateCrmClient.php.html new file mode 100644 index 0000000..396d0f8 --- /dev/null +++ b/tests/coverage/app/Console/Commands/GenerateCrmClient.php.html @@ -0,0 +1,369 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Console/Commands/GenerateCrmClient.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
GenerateCrmClient
n/a
0 / 0
n/a
0 / 0
16
n/a
0 / 0
 handle
n/a
0 / 0
1
n/a
0 / 0
 copyApiConfigInfo
n/a
0 / 0
2
n/a
0 / 0
 copyRoutesList
n/a
0 / 0
4
n/a
0 / 0
 copyExceptions
n/a
0 / 0
1
n/a
0 / 0
 copyEnums
n/a
0 / 0
2
n/a
0 / 0
 copyToFolder
n/a
0 / 0
3
n/a
0 / 0
 replaceInPhpFile
n/a
0 / 0
3
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use App\Enums\ApiError;
6use App\Enums\ApiStatus;
7use App\Enums\ArcadiaOrderType;
8use App\Enums\Country;
9use App\Enums\QuestionType;
10use App\Exceptions\Api\V2\ApiErrorException;
11use App\Exceptions\Api\V2\CrmException;
12use Illuminate\Console\Command;
13use Illuminate\Support\Facades\File;
14use Illuminate\Support\Facades\Route;
15
16/**
17 * @codeCoverageIgnore
18 */
19class GenerateCrmClient extends Command
20{
21    private const TARGET          = 'clients/V2/src';
22    private const CLIENTNAMESPACE = 'LDC\\LDCCRM';
23
24    /**
25     * The name and signature of the console command.
26     *
27     * @var string
28     */
29    protected $signature = 'client:generate';
30
31    /**
32     * The console command description.
33     *
34     * @var string
35     */
36    protected $description = 'Generate the crm v2 client in clients/V2 folder';
37
38    /**
39     * Execute the console command.
40     *
41     * @return integer
42     */
43    public function handle()
44    {
45        $this->copyEnums();
46        $this->copyExceptions();
47        $this->copyRoutesList();
48        $this->copyApiConfigInfo();
49        return 0;
50    }
51
52    /**
53     * Copy config files
54     *
55     * @return void
56     */
57    private function copyApiConfigInfo()
58    {
59        $this->info('Copy Api Config');
60        $rootTarget = app_path('../' . self::TARGET);
61        if (File::exists("$rootTarget/../apiConfig.readonly.json")) {
62            File::delete("$rootTarget/../apiConfig.readonly.json");
63        }
64        File::put(
65            "$rootTarget/../apiConfig.readonly.json",
66            \json_encode(config('api'), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
67        );
68    }
69
70    /**
71     * Copy routes into a json file
72     *
73     * @return void
74     */
75    private function copyRoutesList()
76    {
77        $this->info('Copy Routes');
78        $routes = [];
79        /** @var \Illuminate\Routing\Route */
80        foreach (Route::getRoutes() as $route) {
81            if (\strpos($route->getPrefix(), 'v2') !== false) {
82                $routes[$route->getName()] = [
83                    'uri' => $route->uri(),
84                    'methods' => $route->methods()
85                ];
86            }
87        }
88        $rootTarget = app_path('../' . self::TARGET);
89        if (File::exists("$rootTarget/routes.json")) {
90            File::delete("$rootTarget/routes.json");
91        }
92        File::put(
93            "$rootTarget/routes.json",
94            \json_encode($routes, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
95        );
96    }
97
98    /**
99     * Copy Exeption classes
100     *
101     * @return void
102     */
103    private function copyExceptions(): void
104    {
105        $this->info('Copy Exceptions');
106        $rootTarget = app_path('../' . self::TARGET);
107        $this->copyToFolder([
108            CrmException::class,
109            ApiErrorException::class
110        ], 'Exceptions');
111        $this->replaceInPhpFile(
112            "$rootTarget/Exceptions/ApiErrorException.php",
113            'App\Enums\ApiError',
114            'LDC\LDCCRM\Enums\ApiError'
115        );
116    }
117
118    /**
119     * Copy enums classes
120     *
121     * @return void
122     */
123    private function copyEnums(): void
124    {
125        $this->info('Copy Enums');
126        $classes = [
127            ApiError::class,
128            ApiStatus::class,
129            ArcadiaOrderType::class,
130            Country::class,
131            QuestionType::class
132        ];
133        $this->copyToFolder($classes, 'Enums');
134        $rootTarget = app_path('../' . self::TARGET);
135        foreach ($classes as $class) {
136            $classBasename = class_basename($class);
137            $dest          = "$rootTarget/Enums/$classBasename.php";
138            $this->replaceInPhpFile($dest, 'BaseEnumRoutable', 'BaseEnum');
139        }
140    }
141
142    /**
143     * Copy Classes to specified folder
144     *
145     * @param array  $classes
146     * @param string $folder
147     * @return void
148     */
149    private function copyToFolder(array $classes, string $folder): void
150    {
151        $rootTarget = app_path('../' . self::TARGET);
152        foreach ($classes as $class) {
153            $classFileName = \str_replace("App\\$folder\\", '', sprintf('%s.php', $class));
154            $classFileName = \str_replace('\\', '/', $classFileName);
155            $classBasename = class_basename($class);
156            $source        = \app_path("$folder/$classFileName");
157            $dest          = "$rootTarget/$folder/$classBasename.php";
158            if (File::exists($dest)) {
159                File::delete($dest);
160            }
161            File::copy($source, $dest);
162            $this->replaceInPhpFile($dest, 'namespace App', 'namespace ' . self::CLIENTNAMESPACE);
163        }
164    }
165
166    /**
167     * Replaces a specific string with another on a file
168     *
169     * @param string $source
170     * @param string $search
171     * @param string $replace
172     * @return void
173     */
174    private function replaceInPhpFile(string $source, string $search, string $replace): void
175    {
176        $fContent = File::get($source);
177
178        // Remove trailing \Api\V2 .
179        if ($pos = strpos($fContent, '\Api\V2')) {
180            $fContent = substr_replace($fContent, '', $pos, strlen('\Api\V2'));
181        }
182
183        // Replace namespace top level.
184        $pos = strpos($fContent, $search);
185        if ($pos !== false) {
186            File::put($source, \str_replace($search, $replace, $fContent));
187        }
188    }
189}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Console/Commands/LdcWebStatusChecker.php.html b/tests/coverage/app/Console/Commands/LdcWebStatusChecker.php.html new file mode 100644 index 0000000..97c2b73 --- /dev/null +++ b/tests/coverage/app/Console/Commands/LdcWebStatusChecker.php.html @@ -0,0 +1,192 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Console/Commands/LdcWebStatusChecker.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
LdcWebStatusChecker
n/a
0 / 0
n/a
0 / 0
5
n/a
0 / 0
 handle
n/a
0 / 0
5
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use Illuminate\Console\Command;
6
7/**
8 * Command Name: LDC Web Status Checker
9 * Description: Envoi des infos Serveur/Laravel vers LDC Web
10 * Version: 1.1
11 * Author:  Vincent BRIDIER
12 * Author URI: vincent.bridier@ldc.fr
13 *
14 * @codeCoverageIgnore
15 */
16class LdcWebStatusChecker extends Command
17{
18    /**
19     * The name and signature of the console command.
20     *
21     * @var string
22     */
23    protected $signature = 'ldcweb:statuschecker';
24
25    /**
26     * The console command description.
27     *
28     * @var string
29     */
30    protected $description = 'Envoi des infos Serveur/Laravel vers LDC Web';
31
32    /**
33     * Execute the console command.
34     *
35     * @return mixed
36     */
37    public function handle()
38    {
39        $data         = [];
40        $data['uuid'] = config('ldcweb.project_uuid');
41
42        // Sitemap.xml ?
43        if (file_exists(public_path() . '/sitemap.xml')) {
44            $data['sitemap'] = 'true';
45        } else {
46            $data['sitemap'] = 'false';
47        }
48
49        // Robots.txt ?
50        if (file_exists(public_path() . '/robots.txt')) {
51            $data['robots'] = 'true';
52        } else {
53            $data['robots'] = 'false';
54        }
55
56        // Check logs.
57        $data['logs'] = 0;
58        $patherror    = storage_path('/logs');
59        $files        = glob($patherror . '/*.log');
60        if (count($files) > 0) {
61            foreach ($files as $file) {
62                $data['logs'] = $data['logs'] + filesize($file);
63            }
64        }
65
66        $data['code']['version'] = app()->version();
67
68        // Send by cURL.
69        $url     = 'https://ldc-web.ldc.fr/api/status-checker';
70        $ch      = curl_init($url);
71        $payload = ['token' => 'CE8S52Qq7stf5k8frF4T', 'datas' => json_encode($data)];
72        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
73        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
74        curl_exec($ch);
75        curl_close($ch);
76        return 0;
77    }
78}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Console/Commands/MigrateCrmUsers.php.html b/tests/coverage/app/Console/Commands/MigrateCrmUsers.php.html new file mode 100644 index 0000000..9de3485 --- /dev/null +++ b/tests/coverage/app/Console/Commands/MigrateCrmUsers.php.html @@ -0,0 +1,187 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Console/Commands/MigrateCrmUsers.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
MigrateCrmUsers
n/a
0 / 0
n/a
0 / 0
6
n/a
0 / 0
 handle
n/a
0 / 0
6
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use App\Lib\MigrateOldData;
6use App\Models\Brand;
7use Exception;
8use Illuminate\Console\Command;
9use Illuminate\Support\Facades\DB;
10
11/**
12 * @codeCoverageIgnore
13 */
14class MigrateCrmUsers extends Command
15{
16    /**
17     * The name and signature of the console command.
18     *
19     * @var string
20     */
21    protected $signature = 'migrate:crm';
22
23    /**
24     * The console command description.
25     *
26     * @var string
27     */
28    protected $description = 'Migrate crm users';
29
30
31    /**
32     * Execute the console command.
33     *
34     * @return integer
35     */
36    public function handle()
37    {
38        try {
39            $table = $this->ask('What is the tmp table name with users data ?');
40
41            try {
42                DB::connection(config('database.oldDB'))->table($table)->exists();
43            } catch (Exception $e) {
44                $this->error(sprintf('%s table not found on old crm database', $table));
45                return -1;
46            }
47
48            $brandname = $this->ask('What is the brand name ?');
49            $brand     = Brand::where('name', 'LIKE', "%$brandname%")->first();
50            if (!$brand) {
51                if (!$this->confirm(sprintf('Would you like to use %s ? ', $brandname))) {
52                    $this->info('Command cancelled');
53                    return 0;
54                }
55                if ($this->confirm('Would you like to create a new one ?')) {
56                    $brand       = new Brand();
57                    $brand->name = $brandname;
58                    $brand->save();
59                } else {
60                    $this->info('Command cancelled');
61                    return 0;
62                }
63            }
64
65            $mgOldData = new MigrateOldData();
66            $mgOldData->runMigration($brand, $table);
67        } catch (Exception $e) {
68            $this->error(sprintf('Failed %s', $e));
69            return -1;
70        }//end try
71        return 0;
72    }
73}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Console/Commands/StartStatJobs.php.html b/tests/coverage/app/Console/Commands/StartStatJobs.php.html new file mode 100644 index 0000000..733fff9 --- /dev/null +++ b/tests/coverage/app/Console/Commands/StartStatJobs.php.html @@ -0,0 +1,151 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Console/Commands/StartStatJobs.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StartStatJobs
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 handle
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use App\Jobs\StartStatJobs as JobsStartStatJobs;
6use Illuminate\Console\Command;
7
8/**
9 * @codeCoverageIgnore
10 */
11class StartStatJobs extends Command
12{
13    /**
14     * The name and signature of the console command.
15     *
16     * @var string
17     */
18    protected $signature = 'stats:collect';
19
20    /**
21     * The console command description.
22     *
23     * @var string
24     */
25    protected $description = 'Force push the collect stats jobs into the queue (stats)';
26
27    /**
28     * Execute the console command.
29     *
30     * @return integer
31     */
32    public function handle()
33    {
34        \dispatch(new JobsStartStatJobs())->afterResponse();
35        return 0;
36    }
37}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Console/Commands/UserCreate.php.html b/tests/coverage/app/Console/Commands/UserCreate.php.html new file mode 100644 index 0000000..82ca32e --- /dev/null +++ b/tests/coverage/app/Console/Commands/UserCreate.php.html @@ -0,0 +1,164 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Console/Commands/UserCreate.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
UserCreate
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 handle
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console\Commands;
4
5use App\Models\User;
6use Illuminate\Console\Command;
7use Illuminate\Support\Facades\Hash;
8
9/**
10 * @codeCoverageIgnore
11 */
12class UserCreate extends Command
13{
14    /**
15     * The name and signature of the console command.
16     *
17     * @var string
18     */
19    protected $signature = 'user:create';
20
21    /**
22     * The console command description.
23     *
24     * @var string
25     */
26    protected $description = 'Command description';
27
28    /**
29     * Execute the console command.
30     *
31     * @return integer
32     */
33    public function handle()
34    {
35        $firstname = $this->ask('What is the firstname?');
36        $lastname  = $this->ask('What is the lastname?');
37        $email     = $this->ask('What is the email?');
38        $pwd       = $this->secret('What is the password?');
39
40        $user            = new User();
41        $user->firstname = $firstname;
42        $user->lastname  = $lastname;
43        $user->email     = $email;
44        $user->password  = Hash::make($pwd);
45        $user->save();
46
47        $this->info('User created.');
48        return 0;
49    }
50}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Console/Commands/dashboard.html b/tests/coverage/app/Console/Commands/dashboard.html new file mode 100644 index 0000000..c0305e9 --- /dev/null +++ b/tests/coverage/app/Console/Commands/dashboard.html @@ -0,0 +1,286 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Console/Commands + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Console/Commands/index.html b/tests/coverage/app/Console/Commands/index.html new file mode 100644 index 0000000..e2a97a1 --- /dev/null +++ b/tests/coverage/app/Console/Commands/index.html @@ -0,0 +1,156 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Console/Commands + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
DeleteUnconfirmedCustomers.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
GenerateCrmClient.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
LdcWebStatusChecker.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
MigrateCrmUsers.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StartStatJobs.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
UserCreate.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Console/Kernel.php.html b/tests/coverage/app/Console/Kernel.php.html new file mode 100644 index 0000000..a67309d --- /dev/null +++ b/tests/coverage/app/Console/Kernel.php.html @@ -0,0 +1,167 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Console/Kernel.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
Kernel
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 schedule
n/a
0 / 0
1
n/a
0 / 0
 commands
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Console;
4
5use App\Jobs\StartStatJobs;
6use Illuminate\Console\Scheduling\Schedule;
7use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
8
9/**
10 * @codeCoverageIgnore
11 */
12class Kernel extends ConsoleKernel
13{
14    /**
15     * The Artisan commands provided by your application.
16     *
17     * @var array
18     */
19    protected $commands = [];
20
21    /**
22     * Define the application's command schedule.
23     *
24     * @param  \Illuminate\Console\Scheduling\Schedule $schedule
25     * @return void
26     */
27    protected function schedule(Schedule $schedule)
28    {
29        $schedule->job(StartStatJobs::class)->dailyAt('03:30');
30    }
31
32    /**
33     * Register the commands for the application.
34     *
35     * @return void
36     */
37    protected function commands()
38    {
39        $this->load(__DIR__ . '/Commands');
40
41        require base_path('routes/console.php');
42    }
43}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Console/dashboard.html b/tests/coverage/app/Console/dashboard.html new file mode 100644 index 0000000..02a560e --- /dev/null +++ b/tests/coverage/app/Console/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Console + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Console/index.html b/tests/coverage/app/Console/index.html new file mode 100644 index 0000000..fc1edb6 --- /dev/null +++ b/tests/coverage/app/Console/index.html @@ -0,0 +1,103 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Console + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Commands
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Kernel.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Enums/ApiError.php.html b/tests/coverage/app/Enums/ApiError.php.html new file mode 100644 index 0000000..bee46d1 --- /dev/null +++ b/tests/coverage/app/Enums/ApiError.php.html @@ -0,0 +1,288 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Enums/ApiError.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
ApiError
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 labels
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * This describes an API error.
9 *
10 * @method static self modelNotFound()
11 * The request model was not found (typically the customer)
12 *
13 * @method static self validationFailed()
14 * This means some data could not be validated,
15 * you may have to look into message to get the details
16 *
17 * @method static self customerExists()
18 * The customer already exists
19 *
20 * @method static self customerNotActivated()
21 * The customer account is not activated and cannot perform
22 * this action
23 *
24 * @method static self customerAlreadyActive()
25 * You have requested to activate a customer account,
26 * but it is already active
27 *
28 * @method static self singleOptinCustomerExists()
29 * You have requested to create a single optin for an email,
30 * but it already exists
31 *
32 * @method static self apiAuthFailed()
33 * The authentification to the api failed. You may have to
34 * check into config or .env file for your service credentials
35 *
36 * @method static self authFailed()
37 * The authentification for this customer failed because of wrong credentials
38 *
39 * @method static self methodNotAllowed()
40 * This http method is not allowed
41 *
42 * @method static self wrongTimeStamp()
43 * The timestamp given is wrong. This would happen in case if
44 * a replay attack scenario. Timestamps are provided in headers
45 * and in the http body
46 *
47 * @method static self missingParameter()
48 * The request is missing some parameters
49 *
50 * @method static self throttleRequestsException()
51 * Too many requests have been attempted. The is a throttle security.
52 * Normally you should not trigger this.
53 * If this happens fill a ticket on https://supportweb.ldc.fr
54 * with some technical details
55 *
56 * @method static self authorizationException()
57 * You are maybe trying to access data that is out of your range.
58 * This should not happen normally, it is here to allow you to handle this case.
59 * If this happens fill a ticket on https://supportweb.ldc.fr
60 * with some technical details
61 *
62 * @package LDCCrmClient
63 * @subpackage enums
64 */
65class ApiError extends BaseEnumRoutable
66{
67    /**
68     * Valeurs des possibilités de cet énumération (value)
69     *
70     * @return array
71     */
72    public static function values(): array
73    {
74        return [
75            'modelNotFound' => 101,
76            'validationFailed' => 102,
77            'customerExists' => 103,
78            'customerAlreadyActive' => 110,
79            'customerNotActivated' => 116,
80            'authFailed' => 117,
81            'apiAuthFailed' => 119,
82            'singleOptinCustomerExists' => 120,
83            'methodNotAllowed' => 150,
84            'wrongTimeStamp' => 151,
85            'missingParameter' => 400,
86            'authorizationException' => 403,
87            'throttleRequestsException' => 429
88        ];
89    }
90
91    /**
92     * Libellés des possibilités de cet énumération (label)
93     *
94     * @return array
95     */
96    public static function labels(): array
97    {
98        return [
99            'modelNotFound' => '%s introuvable.',
100            'validationFailed' => 'Impossible de valider les données',
101            'customerExists' => 'L\'utilisateur est déjà enregistré sur ce site',
102            'customerNotActivated' => 'Le compte n\'est pas activé',
103            'customerAlreadyActive' => 'Le compte est déjà activé',
104            'singleOptinCustomerExists' => 'Le single optin ne peut être ajouté car un compte existe.',
105            'authFailed' => 'Echec de l\'authentification du compte',
106            'apiAuthFailed' => 'Echec de l\'authentification client à l\'Api LDC-CRM pour ce service',
107            'methodNotAllowed' => 'This method is not allowed for this request',
108            'wrongTimeStamp' => 'Your request is malformed, please check timestamp header and parameter',
109            'missingParameter' => 'The request is missing some parameters',
110            'authorizationException' => 'You are not autorized to access this',
111            'throttleRequestsException' => 'Throttle: too many request have been attempted, please retry later'
112        ];
113    }
114}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Enums/ApiStatus.php.html b/tests/coverage/app/Enums/ApiStatus.php.html new file mode 100644 index 0000000..a610092 --- /dev/null +++ b/tests/coverage/app/Enums/ApiStatus.php.html @@ -0,0 +1,118 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Enums/ApiStatus.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ApiStatus
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * @method static self OK()
9 * @method static self KO()
10 *
11 * @package LDCCrmClient
12 * @subpackage enums
13 */
14class ApiStatus extends BaseEnumRoutable
15{
16}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Enums/ArcadiaMailType.php.html b/tests/coverage/app/Enums/ArcadiaMailType.php.html new file mode 100644 index 0000000..7263690 --- /dev/null +++ b/tests/coverage/app/Enums/ArcadiaMailType.php.html @@ -0,0 +1,181 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Enums/ArcadiaMailType.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ArcadiaMailType
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * @method static self default()
9 * @method static self game()
10 *
11 * @package LDCCrmClient
12 * @subpackage enums
13 */
14class ArcadiaMailType extends BaseEnumRoutable
15{
16    /**
17     * Valeurs des possibilités de cet énumération (value)
18     *
19     * @return array
20     */
21    public static function values(): array
22    {
23        return [
24            'default' => 1,
25            'game' => 2
26        ];
27    }
28}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Enums/ArcadiaOrderType.php.html b/tests/coverage/app/Enums/ArcadiaOrderType.php.html new file mode 100644 index 0000000..19e44fb --- /dev/null +++ b/tests/coverage/app/Enums/ArcadiaOrderType.php.html @@ -0,0 +1,181 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Enums/ArcadiaOrderType.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ArcadiaOrderType
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * @method static self cb()
9 * @method static self check()
10 *
11 * @package LDCCrmClient
12 * @subpackage enums
13 */
14class ArcadiaOrderType extends BaseEnumRoutable
15{
16    /**
17     * Valeurs des possibilités de cet énumération (value)
18     *
19     * @return array
20     */
21    public static function values(): array
22    {
23        return [
24            'cb' => 0,
25            'check' => 1
26        ];
27    }
28}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Enums/Country.php.html b/tests/coverage/app/Enums/Country.php.html new file mode 100644 index 0000000..15e349f --- /dev/null +++ b/tests/coverage/app/Enums/Country.php.html @@ -0,0 +1,958 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Enums/Country.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
Country
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 labels
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * Liste de Pays
9 * @source https://datahub.io/core/country-list Data Hub country list
10 * @method static self afghanistan()
11 * @method static self alandIslands()
12 * @method static self albania()
13 * @method static self algeria()
14 * @method static self americanSamoa()
15 * @method static self andorra()
16 * @method static self angola()
17 * @method static self anguilla()
18 * @method static self antarctica()
19 * @method static self antiguaAndBarbuda()
20 * @method static self argentina()
21 * @method static self armenia()
22 * @method static self aruba()
23 * @method static self australia()
24 * @method static self austria()
25 * @method static self azerbaijan()
26 * @method static self bahrain()
27 * @method static self bahamas()
28 * @method static self bangladesh()
29 * @method static self barbados()
30 * @method static self belarus()
31 * @method static self belgium()
32 * @method static self belize()
33 * @method static self benin()
34 * @method static self bermuda()
35 * @method static self bhutan()
36 * @method static self boliviaPlurinationalStateOf()
37 * @method static self bonaireSintEustatiusAndSaba()
38 * @method static self bosniaAndHerzegovina()
39 * @method static self botswana()
40 * @method static self bouvetIsland()
41 * @method static self brazil()
42 * @method static self britishIndianOceanTerritory()
43 * @method static self bruneiDarussalam()
44 * @method static self bulgaria()
45 * @method static self burkinaFaso()
46 * @method static self burundi()
47 * @method static self cambodia()
48 * @method static self cameroon()
49 * @method static self canada()
50 * @method static self capeVerde()
51 * @method static self caymanIslands()
52 * @method static self centralAfricanRepublic()
53 * @method static self chad()
54 * @method static self chile()
55 * @method static self china()
56 * @method static self christmasIsland()
57 * @method static self cocosKeelingIslands()
58 * @method static self colombia()
59 * @method static self comoros()
60 * @method static self congo()
61 * @method static self congoTheDemocraticRepublicOfThe()
62 * @method static self cookIslands()
63 * @method static self costaRica()
64 * @method static self coteDivoire()
65 * @method static self croatia()
66 * @method static self cuba()
67 * @method static self curacao()
68 * @method static self cyprus()
69 * @method static self czechRepublic()
70 * @method static self denmark()
71 * @method static self djibouti()
72 * @method static self dominica()
73 * @method static self dominicanRepublic()
74 * @method static self ecuador()
75 * @method static self egypt()
76 * @method static self elSalvador()
77 * @method static self equatorialGuinea()
78 * @method static self eritrea()
79 * @method static self estonia()
80 * @method static self ethiopia()
81 * @method static self falklandIslandsMalvinas()
82 * @method static self faroeIslands()
83 * @method static self fiji()
84 * @method static self finland()
85 * @method static self france()
86 * @method static self frenchGuiana()
87 * @method static self frenchPolynesia()
88 * @method static self frenchSouthernTerritories()
89 * @method static self gabon()
90 * @method static self gambia()
91 * @method static self georgia()
92 * @method static self germany()
93 * @method static self ghana()
94 * @method static self gibraltar()
95 * @method static self greece()
96 * @method static self greenland()
97 * @method static self grenada()
98 * @method static self guadeloupe()
99 * @method static self guam()
100 * @method static self guatemala()
101 * @method static self guernsey()
102 * @method static self guinea()
103 * @method static self guineaBissau()
104 * @method static self guyana()
105 * @method static self haiti()
106 * @method static self heardIslandAndMcdonaldIslands()
107 * @method static self holySeeVaticanCityState()
108 * @method static self honduras()
109 * @method static self hongKong()
110 * @method static self hungary()
111 * @method static self iceland()
112 * @method static self india()
113 * @method static self indonesia()
114 * @method static self iranIslamicRepublicOf()
115 * @method static self iraq()
116 * @method static self ireland()
117 * @method static self isleOfMan()
118 * @method static self israel()
119 * @method static self italy()
120 * @method static self jamaica()
121 * @method static self japan()
122 * @method static self jersey()
123 * @method static self jordan()
124 * @method static self kazakhstan()
125 * @method static self kenya()
126 * @method static self kiribati()
127 * @method static self koreaDemocraticPeoplesRepublicOf()
128 * @method static self koreaRepublicOf()
129 * @method static self kuwait()
130 * @method static self kyrgyzstan()
131 * @method static self laoPeoplesDemocraticRepublic()
132 * @method static self latvia()
133 * @method static self lebanon()
134 * @method static self lesotho()
135 * @method static self liberia()
136 * @method static self libya()
137 * @method static self liechtenstein()
138 * @method static self lithuania()
139 * @method static self luxembourg()
140 * @method static self macao()
141 * @method static self macedoniaTheFormerYugoslavRepublicOf()
142 * @method static self madagascar()
143 * @method static self malawi()
144 * @method static self malaysia()
145 * @method static self maldives()
146 * @method static self mali()
147 * @method static self malta()
148 * @method static self marshallIslands()
149 * @method static self martinique()
150 * @method static self mauritania()
151 * @method static self mauritius()
152 * @method static self mayotte()
153 * @method static self mexico()
154 * @method static self micronesiaFederatedStatesOf()
155 * @method static self moldovaRepublicOf()
156 * @method static self monaco()
157 * @method static self mongolia()
158 * @method static self montenegro()
159 * @method static self montserrat()
160 * @method static self morocco()
161 * @method static self mozambique()
162 * @method static self myanmar()
163 * @method static self namibia()
164 * @method static self nauru()
165 * @method static self nepal()
166 * @method static self netherlands()
167 * @method static self newCaledonia()
168 * @method static self newZealand()
169 * @method static self nicaragua()
170 * @method static self niger()
171 * @method static self nigeria()
172 * @method static self niue()
173 * @method static self norfolkIsland()
174 * @method static self northernMarianaIslands()
175 * @method static self norway()
176 * @method static self oman()
177 * @method static self pakistan()
178 * @method static self palau()
179 * @method static self palestineStateOf()
180 * @method static self panama()
181 * @method static self papuaNewGuinea()
182 * @method static self paraguay()
183 * @method static self peru()
184 * @method static self philippines()
185 * @method static self pitcairn()
186 * @method static self poland()
187 * @method static self portugal()
188 * @method static self puertoRico()
189 * @method static self qatar()
190 * @method static self reunion()
191 * @method static self romania()
192 * @method static self russianFederation()
193 * @method static self rwanda()
194 * @method static self saintBarthelemy()
195 * @method static self saintHelenaAscensionAndTristanDaCunha()
196 * @method static self saintKittsAndNevis()
197 * @method static self saintLucia()
198 * @method static self saintMartinFrenchPart()
199 * @method static self saintPierreAndMiquelon()
200 * @method static self saintVincentAndTheGrenadines()
201 * @method static self samoa()
202 * @method static self sanMarino()
203 * @method static self saoTomeAndPrincipe()
204 * @method static self saudiArabia()
205 * @method static self senegal()
206 * @method static self serbia()
207 * @method static self seychelles()
208 * @method static self sierraLeone()
209 * @method static self singapore()
210 * @method static self sintMaartenDutchPart()
211 * @method static self slovakia()
212 * @method static self slovenia()
213 * @method static self solomonIslands()
214 * @method static self somalia()
215 * @method static self southAfrica()
216 * @method static self southGeorgiaAndTheSouthSandwichIslands()
217 * @method static self southSudan()
218 * @method static self spain()
219 * @method static self sriLanka()
220 * @method static self sudan()
221 * @method static self suriname()
222 * @method static self svalbardAndJanMayen()
223 * @method static self swaziland()
224 * @method static self sweden()
225 * @method static self switzerland()
226 * @method static self syrianArabRepublic()
227 * @method static self taiwanProvinceOfChina()
228 * @method static self tajikistan()
229 * @method static self tanzaniaUnitedRepublicOf()
230 * @method static self thailand()
231 * @method static self timorLeste()
232 * @method static self togo()
233 * @method static self tokelau()
234 * @method static self tonga()
235 * @method static self trinidadAndTobago()
236 * @method static self tunisia()
237 * @method static self turkey()
238 * @method static self turkmenistan()
239 * @method static self turksAndCaicosIslands()
240 * @method static self tuvalu()
241 * @method static self uganda()
242 * @method static self ukraine()
243 * @method static self unitedArabEmirates()
244 * @method static self unitedKingdom()
245 * @method static self unitedStates()
246 * @method static self unitedStatesMinorOutlyingIslands()
247 * @method static self uruguay()
248 * @method static self uzbekistan()
249 * @method static self vanuatu()
250 * @method static self venezuelaBolivarianRepublicOf()
251 * @method static self vietNam()
252 * @method static self virginIslandsBritish()
253 * @method static self virginIslandsUs()
254 * @method static self wallisAndFutuna()
255 * @method static self westernSahara()
256 * @method static self yemen()
257 * @method static self zambia()
258 * @method static self zimbabwe()
259 *
260 * @package LDCCrmClient
261 * @subpackage enums
262 */
263class Country extends BaseEnumRoutable
264{
265    /**
266     * Valeurs des possibilités de cet énumération (value)
267     *
268     * @return array
269     */
270    public static function values(): array
271    {
272        return [
273            'afghanistan' => 1,
274            'alandIslands' => 2,
275            'albania' => 3,
276            'algeria' => 4,
277            'americanSamoa' => 5,
278            'andorra' => 6,
279            'angola' => 7,
280            'anguilla' => 8,
281            'antarctica' => 9,
282            'antiguaAndBarbuda' => 10,
283            'argentina' => 11,
284            'armenia' => 12,
285            'aruba' => 13,
286            'australia' => 14,
287            'austria' => 15,
288            'azerbaijan' => 16,
289            'bahrain' => 17,
290            'bahamas' => 18,
291            'bangladesh' => 19,
292            'barbados' => 20,
293            'belarus' => 21,
294            'belgium' => 22,
295            'belize' => 23,
296            'benin' => 24,
297            'bermuda' => 25,
298            'bhutan' => 26,
299            'boliviaPlurinationalStateOf' => 27,
300            'bonaireSintEustatiusAndSaba' => 28,
301            'bosniaAndHerzegovina' => 29,
302            'botswana' => 30,
303            'bouvetIsland' => 31,
304            'brazil' => 32,
305            'britishIndianOceanTerritory' => 33,
306            'bruneiDarussalam' => 34,
307            'bulgaria' => 35,
308            'burkinaFaso' => 36,
309            'burundi' => 37,
310            'cambodia' => 38,
311            'cameroon' => 39,
312            'canada' => 40,
313            'capeVerde' => 41,
314            'caymanIslands' => 42,
315            'centralAfricanRepublic' => 43,
316            'chad' => 44,
317            'chile' => 45,
318            'china' => 46,
319            'christmasIsland' => 47,
320            'cocosKeelingIslands' => 48,
321            'colombia' => 49,
322            'comoros' => 50,
323            'congo' => 51,
324            'congoTheDemocraticRepublicOfThe' => 52,
325            'cookIslands' => 53,
326            'costaRica' => 54,
327            'coteDivoire' => 55,
328            'croatia' => 56,
329            'cuba' => 57,
330            'curacao' => 58,
331            'cyprus' => 59,
332            'czechRepublic' => 60,
333            'denmark' => 61,
334            'djibouti' => 62,
335            'dominica' => 63,
336            'dominicanRepublic' => 64,
337            'ecuador' => 65,
338            'egypt' => 66,
339            'elSalvador' => 67,
340            'equatorialGuinea' => 68,
341            'eritrea' => 69,
342            'estonia' => 70,
343            'ethiopia' => 71,
344            'falklandIslandsMalvinas' => 72,
345            'faroeIslands' => 73,
346            'fiji' => 74,
347            'finland' => 75,
348            'france' => 76,
349            'frenchGuiana' => 77,
350            'frenchPolynesia' => 78,
351            'frenchSouthernTerritories' => 79,
352            'gabon' => 80,
353            'gambia' => 81,
354            'georgia' => 82,
355            'germany' => 83,
356            'ghana' => 84,
357            'gibraltar' => 85,
358            'greece' => 86,
359            'greenland' => 87,
360            'grenada' => 88,
361            'guadeloupe' => 89,
362            'guam' => 90,
363            'guatemala' => 91,
364            'guernsey' => 92,
365            'guinea' => 93,
366            'guineaBissau' => 94,
367            'guyana' => 95,
368            'haiti' => 96,
369            'heardIslandAndMcdonaldIslands' => 97,
370            'holySeeVaticanCityState' => 98,
371            'honduras' => 99,
372            'hongKong' => 100,
373            'hungary' => 101,
374            'iceland' => 102,
375            'india' => 103,
376            'indonesia' => 104,
377            'iranIslamicRepublicOf' => 105,
378            'iraq' => 106,
379            'ireland' => 107,
380            'isleOfMan' => 108,
381            'israel' => 109,
382            'italy' => 110,
383            'jamaica' => 111,
384            'japan' => 112,
385            'jersey' => 113,
386            'jordan' => 114,
387            'kazakhstan' => 115,
388            'kenya' => 116,
389            'kiribati' => 117,
390            'koreaDemocraticPeoplesRepublicOf' => 118,
391            'koreaRepublicOf' => 119,
392            'kuwait' => 120,
393            'kyrgyzstan' => 121,
394            'laoPeoplesDemocraticRepublic' => 122,
395            'latvia' => 123,
396            'lebanon' => 124,
397            'lesotho' => 125,
398            'liberia' => 126,
399            'libya' => 127,
400            'liechtenstein' => 128,
401            'lithuania' => 129,
402            'luxembourg' => 130,
403            'macao' => 131,
404            'macedoniaTheFormerYugoslavRepublicOf' => 132,
405            'madagascar' => 133,
406            'malawi' => 134,
407            'malaysia' => 135,
408            'maldives' => 136,
409            'mali' => 137,
410            'malta' => 138,
411            'marshallIslands' => 139,
412            'martinique' => 140,
413            'mauritania' => 141,
414            'mauritius' => 142,
415            'mayotte' => 143,
416            'mexico' => 144,
417            'micronesiaFederatedStatesOf' => 145,
418            'moldovaRepublicOf' => 146,
419            'monaco' => 147,
420            'mongolia' => 148,
421            'montenegro' => 149,
422            'montserrat' => 150,
423            'morocco' => 151,
424            'mozambique' => 152,
425            'myanmar' => 153,
426            'namibia' => 154,
427            'nauru' => 155,
428            'nepal' => 156,
429            'netherlands' => 157,
430            'newCaledonia' => 158,
431            'newZealand' => 159,
432            'nicaragua' => 160,
433            'niger' => 161,
434            'nigeria' => 162,
435            'niue' => 163,
436            'norfolkIsland' => 164,
437            'northernMarianaIslands' => 165,
438            'norway' => 166,
439            'oman' => 167,
440            'pakistan' => 168,
441            'palau' => 169,
442            'palestineStateOf' => 170,
443            'panama' => 171,
444            'papuaNewGuinea' => 172,
445            'paraguay' => 173,
446            'peru' => 174,
447            'philippines' => 175,
448            'pitcairn' => 176,
449            'poland' => 177,
450            'portugal' => 178,
451            'puertoRico' => 179,
452            'qatar' => 180,
453            'reunion' => 181,
454            'romania' => 182,
455            'russianFederation' => 183,
456            'rwanda' => 184,
457            'saintBarthelemy' => 185,
458            'saintHelenaAscensionAndTristanDaCunha' => 186,
459            'saintKittsAndNevis' => 187,
460            'saintLucia' => 188,
461            'saintMartinFrenchPart' => 189,
462            'saintPierreAndMiquelon' => 190,
463            'saintVincentAndTheGrenadines' => 191,
464            'samoa' => 192,
465            'sanMarino' => 193,
466            'saoTomeAndPrincipe' => 194,
467            'saudiArabia' => 195,
468            'senegal' => 196,
469            'serbia' => 197,
470            'seychelles' => 198,
471            'sierraLeone' => 199,
472            'singapore' => 200,
473            'sintMaartenDutchPart' => 201,
474            'slovakia' => 202,
475            'slovenia' => 203,
476            'solomonIslands' => 204,
477            'somalia' => 205,
478            'southAfrica' => 206,
479            'southGeorgiaAndTheSouthSandwichIslands' => 207,
480            'southSudan' => 208,
481            'spain' => 209,
482            'sriLanka' => 210,
483            'sudan' => 211,
484            'suriname' => 212,
485            'svalbardAndJanMayen' => 213,
486            'swaziland' => 214,
487            'sweden' => 215,
488            'switzerland' => 216,
489            'syrianArabRepublic' => 217,
490            'taiwanProvinceOfChina' => 218,
491            'tajikistan' => 219,
492            'tanzaniaUnitedRepublicOf' => 220,
493            'thailand' => 221,
494            'timorLeste' => 222,
495            'togo' => 223,
496            'tokelau' => 224,
497            'tonga' => 225,
498            'trinidadAndTobago' => 226,
499            'tunisia' => 227,
500            'turkey' => 228,
501            'turkmenistan' => 229,
502            'turksAndCaicosIslands' => 230,
503            'tuvalu' => 231,
504            'uganda' => 232,
505            'ukraine' => 233,
506            'unitedArabEmirates' => 234,
507            'unitedKingdom' => 235,
508            'unitedStates' => 236,
509            'unitedStatesMinorOutlyingIslands' => 237,
510            'uruguay' => 238,
511            'uzbekistan' => 239,
512            'vanuatu' => 240,
513            'venezuelaBolivarianRepublicOf' => 241,
514            'vietNam' => 242,
515            'virginIslandsBritish' => 243,
516            'virginIslandsUs' => 244,
517            'wallisAndFutuna' => 245,
518            'westernSahara' => 246,
519            'yemen' => 247,
520            'zambia' => 248,
521            'zimbabwe' => 249
522        ];
523    }
524
525    /**
526     * Libellés des possibilités de cet énumération (label)
527     *
528     * @return array
529     */
530    public static function labels(): array
531    {
532        return [
533            'afghanistan' => 'Afghanistan',
534            'alandIslands' => 'Åland Islands',
535            'albania' => 'Albania',
536            'algeria' => 'Algeria',
537            'americanSamoa' => 'American Samoa',
538            'andorra' => 'Andorra',
539            'angola' => 'Angola',
540            'anguilla' => 'Anguilla',
541            'antarctica' => 'Antarctica',
542            'antiguaAndBarbuda' => 'Antigua and Barbuda',
543            'argentina' => 'Argentina',
544            'armenia' => 'Armenia',
545            'aruba' => 'Aruba',
546            'australia' => 'Australia',
547            'austria' => 'Austria',
548            'azerbaijan' => 'Azerbaijan',
549            'bahrain' => 'Bahrain',
550            'bahamas' => 'Bahamas',
551            'bangladesh' => 'Bangladesh',
552            'barbados' => 'Barbados',
553            'belarus' => 'Belarus',
554            'belgium' => 'Belgium',
555            'belize' => 'Belize',
556            'benin' => 'Benin',
557            'bermuda' => 'Bermuda',
558            'bhutan' => 'Bhutan',
559            'boliviaPlurinationalStateOf' => 'Bolivia, Plurinational State of',
560            'bonaireSintEustatiusAndSaba' => 'Bonaire, Sint Eustatius and Saba',
561            'bosniaAndHerzegovina' => 'Bosnia and Herzegovina',
562            'botswana' => 'Botswana',
563            'bouvetIsland' => 'Bouvet Island',
564            'brazil' => 'Brazil',
565            'britishIndianOceanTerritory' => 'British Indian Ocean Territory',
566            'bruneiDarussalam' => 'Brunei Darussalam',
567            'bulgaria' => 'Bulgaria',
568            'burkinaFaso' => 'Burkina Faso',
569            'burundi' => 'Burundi',
570            'cambodia' => 'Cambodia',
571            'cameroon' => 'Cameroon',
572            'canada' => 'Canada',
573            'capeVerde' => 'Cape Verde',
574            'caymanIslands' => 'Cayman Islands',
575            'centralAfricanRepublic' => 'Central African Republic',
576            'chad' => 'Chad',
577            'chile' => 'Chile',
578            'china' => 'China',
579            'christmasIsland' => 'Christmas Island',
580            'cocosKeelingIslands' => 'Cocos (Keeling) Islands',
581            'colombia' => 'Colombia',
582            'comoros' => 'Comoros',
583            'congo' => 'Congo',
584            'congoTheDemocraticRepublicOfThe' => 'Congo, the Democratic Republic of the',
585            'cookIslands' => 'Cook Islands',
586            'costaRica' => 'Costa Rica',
587            'coteDivoire' => 'Côte d\'Ivoire',
588            'croatia' => 'Croatia',
589            'cuba' => 'Cuba',
590            'curacao' => 'Curaçao',
591            'cyprus' => 'Cyprus',
592            'czechRepublic' => 'Czech Republic',
593            'denmark' => 'Denmark',
594            'djibouti' => 'Djibouti',
595            'dominica' => 'Dominica',
596            'dominicanRepublic' => 'Dominican Republic',
597            'ecuador' => 'Ecuador',
598            'egypt' => 'Egypt',
599            'elSalvador' => 'El Salvador',
600            'equatorialGuinea' => 'Equatorial Guinea',
601            'eritrea' => 'Eritrea',
602            'estonia' => 'Estonia',
603            'ethiopia' => 'Ethiopia',
604            'falklandIslandsMalvinas' => 'Falkland Islands (Malvinas)',
605            'faroeIslands' => 'Faroe Islands',
606            'fiji' => 'Fiji',
607            'finland' => 'Finland',
608            'france' => 'France',
609            'frenchGuiana' => 'French Guiana',
610            'frenchPolynesia' => 'French Polynesia',
611            'frenchSouthernTerritories' => 'French Southern Territories',
612            'gabon' => 'Gabon',
613            'gambia' => 'Gambia',
614            'georgia' => 'Georgia',
615            'germany' => 'Germany',
616            'ghana' => 'Ghana',
617            'gibraltar' => 'Gibraltar',
618            'greece' => 'Greece',
619            'greenland' => 'Greenland',
620            'grenada' => 'Grenada',
621            'guadeloupe' => 'Guadeloupe',
622            'guam' => 'Guam',
623            'guatemala' => 'Guatemala',
624            'guernsey' => 'Guernsey',
625            'guinea' => 'Guinea',
626            'guineaBissau' => 'GuineaBissau',
627            'guyana' => 'Guyana',
628            'haiti' => 'Haiti',
629            'heardIslandAndMcdonaldIslands' => 'Heard Island and McDonald Islands',
630            'holySeeVaticanCityState' => 'Holy See (Vatican City State)',
631            'honduras' => 'Honduras',
632            'hongKong' => 'Hong Kong',
633            'hungary' => 'Hungary',
634            'iceland' => 'Iceland',
635            'india' => 'India',
636            'indonesia' => 'Indonesia',
637            'iranIslamicRepublicOf' => 'Iran, Islamic Republic of',
638            'iraq' => 'Iraq',
639            'ireland' => 'Ireland',
640            'isleOfMan' => 'Isle of Man',
641            'israel' => 'Israel',
642            'italy' => 'Italy',
643            'jamaica' => 'Jamaica',
644            'japan' => 'Japan',
645            'jersey' => 'Jersey',
646            'jordan' => 'Jordan',
647            'kazakhstan' => 'Kazakhstan',
648            'kenya' => 'Kenya',
649            'kiribati' => 'Kiribati',
650            'koreaDemocraticPeoplesRepublicOf' => 'Korea, Democratic People\'s Republic of',
651            'koreaRepublicOf' => 'Korea, Republic of',
652            'kuwait' => 'Kuwait',
653            'kyrgyzstan' => 'Kyrgyzstan',
654            'laoPeoplesDemocraticRepublic' => 'Lao People\'s Democratic Republic',
655            'latvia' => 'Latvia',
656            'lebanon' => 'Lebanon',
657            'lesotho' => 'Lesotho',
658            'liberia' => 'Liberia',
659            'libya' => 'Libya',
660            'liechtenstein' => 'Liechtenstein',
661            'lithuania' => 'Lithuania',
662            'luxembourg' => 'Luxembourg',
663            'macao' => 'Macao',
664            'macedoniaTheFormerYugoslavRepublicOf' => 'Macedonia, the Former Yugoslav Republic of',
665            'madagascar' => 'Madagascar',
666            'malawi' => 'Malawi',
667            'malaysia' => 'Malaysia',
668            'maldives' => 'Maldives',
669            'mali' => 'Mali',
670            'malta' => 'Malta',
671            'marshallIslands' => 'Marshall Islands',
672            'martinique' => 'Martinique',
673            'mauritania' => 'Mauritania',
674            'mauritius' => 'Mauritius',
675            'mayotte' => 'Mayotte',
676            'mexico' => 'Mexico',
677            'micronesiaFederatedStatesOf' => 'Micronesia, Federated States of',
678            'moldovaRepublicOf' => 'Moldova, Republic of',
679            'monaco' => 'Monaco',
680            'mongolia' => 'Mongolia',
681            'montenegro' => 'Montenegro',
682            'montserrat' => 'Montserrat',
683            'morocco' => 'Morocco',
684            'mozambique' => 'Mozambique',
685            'myanmar' => 'Myanmar',
686            'namibia' => 'Namibia',
687            'nauru' => 'Nauru',
688            'nepal' => 'Nepal',
689            'netherlands' => 'Netherlands',
690            'newCaledonia' => 'New Caledonia',
691            'newZealand' => 'New Zealand',
692            'nicaragua' => 'Nicaragua',
693            'niger' => 'Niger',
694            'nigeria' => 'Nigeria',
695            'niue' => 'Niue',
696            'norfolkIsland' => 'Norfolk Island',
697            'northernMarianaIslands' => 'Northern Mariana Islands',
698            'norway' => 'Norway',
699            'oman' => 'Oman',
700            'pakistan' => 'Pakistan',
701            'palau' => 'Palau',
702            'palestineStateOf' => 'Palestine, State of',
703            'panama' => 'Panama',
704            'papuaNewGuinea' => 'Papua New Guinea',
705            'paraguay' => 'Paraguay',
706            'peru' => 'Peru',
707            'philippines' => 'Philippines',
708            'pitcairn' => 'Pitcairn',
709            'poland' => 'Poland',
710            'portugal' => 'Portugal',
711            'puertoRico' => 'Puerto Rico',
712            'qatar' => 'Qatar',
713            'reunion' => 'Réunion',
714            'romania' => 'Romania',
715            'russianFederation' => 'Russian Federation',
716            'rwanda' => 'Rwanda',
717            'saintBarthelemy' => 'Saint Barthélemy',
718            'saintHelenaAscensionAndTristanDaCunha' => 'Saint Helena, Ascension and Tristan da Cunha',
719            'saintKittsAndNevis' => 'Saint Kitts and Nevis',
720            'saintLucia' => 'Saint Lucia',
721            'saintMartinFrenchPart' => 'Saint Martin (French part)',
722            'saintPierreAndMiquelon' => 'Saint Pierre and Miquelon',
723            'saintVincentAndTheGrenadines' => 'Saint Vincent and the Grenadines',
724            'samoa' => 'Samoa',
725            'sanMarino' => 'San Marino',
726            'saoTomeAndPrincipe' => 'Sao Tome and Principe',
727            'saudiArabia' => 'Saudi Arabia',
728            'senegal' => 'Senegal',
729            'serbia' => 'Serbia',
730            'seychelles' => 'Seychelles',
731            'sierraLeone' => 'Sierra Leone',
732            'singapore' => 'Singapore',
733            'sintMaartenDutchPart' => 'Sint Maarten (Dutch part)',
734            'slovakia' => 'Slovakia',
735            'slovenia' => 'Slovenia',
736            'solomonIslands' => 'Solomon Islands',
737            'somalia' => 'Somalia',
738            'southAfrica' => 'South Africa',
739            'southGeorgiaAndTheSouthSandwichIslands' => 'South Georgia and the South Sandwich Islands',
740            'southSudan' => 'South Sudan',
741            'spain' => 'Spain',
742            'sriLanka' => 'Sri Lanka',
743            'sudan' => 'Sudan',
744            'suriname' => 'Suriname',
745            'svalbardAndJanMayen' => 'Svalbard and Jan Mayen',
746            'swaziland' => 'Swaziland',
747            'sweden' => 'Sweden',
748            'switzerland' => 'Switzerland',
749            'syrianArabRepublic' => 'Syrian Arab Republic',
750            'taiwanProvinceOfChina' => 'Taiwan, Province of China',
751            'tajikistan' => 'Tajikistan',
752            'tanzaniaUnitedRepublicOf' => 'Tanzania, United Republic of',
753            'thailand' => 'Thailand',
754            'timorLeste' => 'TimorLeste',
755            'togo' => 'Togo',
756            'tokelau' => 'Tokelau',
757            'tonga' => 'Tonga',
758            'trinidadAndTobago' => 'Trinidad and Tobago',
759            'tunisia' => 'Tunisia',
760            'turkey' => 'Turkey',
761            'turkmenistan' => 'Turkmenistan',
762            'turksAndCaicosIslands' => 'Turks and Caicos Islands',
763            'tuvalu' => 'Tuvalu',
764            'uganda' => 'Uganda',
765            'ukraine' => 'Ukraine',
766            'unitedArabEmirates' => 'United Arab Emirates',
767            'unitedKingdom' => 'United Kingdom',
768            'unitedStates' => 'United States',
769            'unitedStatesMinorOutlyingIslands' => 'United States Minor Outlying Islands',
770            'uruguay' => 'Uruguay',
771            'uzbekistan' => 'Uzbekistan',
772            'vanuatu' => 'Vanuatu',
773            'venezuelaBolivarianRepublicOf' => 'Venezuela, Bolivarian Republic of',
774            'vietNam' => 'Viet Nam',
775            'virginIslandsBritish' => 'Virgin Islands, British',
776            'virginIslandsUs' => 'Virgin Islands, U.S.',
777            'wallisAndFutuna' => 'Wallis and Futuna',
778            'westernSahara' => 'Western Sahara',
779            'yemen' => 'Yemen',
780            'zambia' => 'Zambia',
781            'zimbabwe' => 'Zimbabwe'
782        ];
783    }
784}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Enums/CustomerExportType.php.html b/tests/coverage/app/Enums/CustomerExportType.php.html new file mode 100644 index 0000000..09e98c0 --- /dev/null +++ b/tests/coverage/app/Enums/CustomerExportType.php.html @@ -0,0 +1,167 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Enums/CustomerExportType.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerExportType
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 values
n/a
0 / 0
1
n/a
0 / 0
 labels
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * @method static self singleOptin()
9 * @method static self customer()
10 * @method static self both()
11 *
12 * @codeCoverageIgnore
13 */
14class CustomerExportType extends BaseEnumRoutable
15{
16    /**
17     * Valeurs des possibilités de cet énumération (value)
18     *
19     * @return array
20     */
21    public static function values(): array
22    {
23        return [
24            'singleOptin' => 1,
25            'customer' => 2,
26            'both' => 3
27        ];
28    }
29
30    /**
31     * Libellés des possibilités de cet énumération (label)
32     *
33     * @return array
34     */
35    public static function labels(): array
36    {
37        return [
38            'singleOptin' => trans('Les single optins uniquement'),
39            'customer' => trans('Les clients uniquement acceptant de recevoir les newsletters'),
40            'both' => trans('La totalité des client acceptant de recevoir les newsletters')
41        ];
42    }
43}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Enums/QuestionType.php.html b/tests/coverage/app/Enums/QuestionType.php.html new file mode 100644 index 0000000..9ce28bb --- /dev/null +++ b/tests/coverage/app/Enums/QuestionType.php.html @@ -0,0 +1,183 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Enums/QuestionType.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
QuestionType
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Enums;
4
5use Kwaadpepper\Enum\BaseEnumRoutable;
6
7/**
8 * @method static self select()
9 * @method static self radio()
10 * @method static self checkbox()
11 *
12 * @package LDCCrmClient
13 * @subpackage enums
14 */
15class QuestionType extends BaseEnumRoutable
16{
17    /**
18     * Valeurs des possibilités de cet énumération (value)
19     *
20     * @return array
21     */
22    public static function values(): array
23    {
24        return [
25            'select' => 1,
26            'radio' => 2,
27            'checkbox' => 3
28        ];
29    }
30}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Enums/dashboard.html b/tests/coverage/app/Enums/dashboard.html new file mode 100644 index 0000000..69f669a --- /dev/null +++ b/tests/coverage/app/Enums/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Enums + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Enums/index.html b/tests/coverage/app/Enums/index.html new file mode 100644 index 0000000..1cda330 --- /dev/null +++ b/tests/coverage/app/Enums/index.html @@ -0,0 +1,258 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Enums + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
ApiError.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiStatus.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ArcadiaMailType.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ArcadiaOrderType.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Country.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CustomerExportType.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
QuestionType.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/app/Exceptions/AbortRowImportException.php.html b/tests/coverage/app/Exceptions/AbortRowImportException.php.html new file mode 100644 index 0000000..7b9b07f --- /dev/null +++ b/tests/coverage/app/Exceptions/AbortRowImportException.php.html @@ -0,0 +1,116 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Exceptions/AbortRowImportException.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AbortRowImportException
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Exceptions;
4
5use Exception;
6
7/**
8 * This class Exception is used to skip a row import from
9 * database migration (2016 -> 2020)
10 * @author Jérémy Munsch <jeremy.munsch@ldc.fr>
11 */
12class AbortRowImportException extends Exception
13{
14}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Exceptions/Api/V2/ApiErrorException.php.html b/tests/coverage/app/Exceptions/Api/V2/ApiErrorException.php.html new file mode 100644 index 0000000..b8a2e88 --- /dev/null +++ b/tests/coverage/app/Exceptions/Api/V2/ApiErrorException.php.html @@ -0,0 +1,322 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Exceptions/Api/V2/ApiErrorException.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
CRAP
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
ApiErrorException
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
5.20
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 getApiError
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 getStatusCode
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 report
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Exceptions\Api\V2;
4
5use App\Enums\ApiError;
6use Exception;
7use Illuminate\Support\Facades\Log;
8use Throwable;
9
10/**
11 * This would be thrown if any of the case
12 * referenced in \App\Enums\ApiError would
13 * occur within the crm
14 *
15 * On the client an ApiErrorException would
16 * also be throw with the ApiError corresponding to
17 * the situation
18 *
19 * @package LDCCrmClient
20 * @subpackage exceptions
21 */
22class ApiErrorException extends Exception
23{
24    /**
25     * Api Error
26     * This value makes the error more explicit.
27     * Handling these errors are normal as inputs or
28     * request may be undesired or refused by the crm.
29     *
30     * @var \App\Enums\ApiError
31     */
32    protected $error;
33
34    /**
35     * This is an explicit error message,
36     * ou may print this to the front or not depending
37     * on $error value
38     *
39     * @var string
40     */
41    protected $message;
42
43    /**
44     * This is the http code returned by the crm.
45     * This value could be used to handle errors.
46     * But please take in consideration $error first
47     *
48     * @var integer
49     */
50    protected $httpCode;
51
52    /**
53     * Create a new exception base on ApiError enum.
54     *
55     * @param ApiError       $error
56     * @param integer        $httpCode
57     * @param string         $message
58     * @param Throwable|null $previous
59     */
60    public function __construct(ApiError $error, int $httpCode = 400, string $message = '', Throwable $previous = null)
61    {
62        $this->error    = $error;
63        $this->message  = $message;
64        $this->httpCode = $httpCode;
65        parent::__construct(
66            strlen($this->message) ? $this->message : $error->label,
67            $error->value,
68            $previous
69        );
70    }
71
72    /**
73     * Get the Api Error
74     *
75     * @return ApiError
76     */
77    public function getApiError(): ApiError
78    {
79        return $this->error;
80    }
81
82    /**
83     * Get the http code
84     *
85     * @return integer
86     */
87    public function getStatusCode(): int
88    {
89        return $this->httpCode;
90    }
91
92    /**
93     * This is used by laravel only.
94     * It uses Log class to print the error.
95     *
96     * @return void
97     */
98    public function report()
99    {
100        // @codeCoverageIgnoreStart
101        Log::debug(sprintf('CRM API: %s', $this->getMessage()));
102        // @codeCoverageIgnoreEnd
103    }
104}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Exceptions/Api/V2/CrmException.php.html b/tests/coverage/app/Exceptions/Api/V2/CrmException.php.html new file mode 100644 index 0000000..617ef6e --- /dev/null +++ b/tests/coverage/app/Exceptions/Api/V2/CrmException.php.html @@ -0,0 +1,144 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Exceptions/Api/V2/CrmException.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CrmException
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 report
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Exceptions\Api\V2;
4
5use Exception;
6use Illuminate\Support\Facades\Log;
7
8/**
9 * This is a critical error.
10 * The request could not be handled. You may dump any value you can
11 * get before submitting a ticker to https://supportweb.ldc.fr
12 *
13 * @package LDCCrmClient
14 * @subpackage exceptions
15 * @codeCoverageIgnore
16 */
17class CrmException extends Exception
18{
19    /**
20     * This is used by laravel only.
21     * It uses Log class to print the error.
22     *
23     * @return void
24     */
25    public function report()
26    {
27        Log::critical(sprintf('CRM API: %s', $this->getMessage()));
28    }
29}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Exceptions/Api/V2/dashboard.html b/tests/coverage/app/Exceptions/Api/V2/dashboard.html new file mode 100644 index 0000000..4df8f40 --- /dev/null +++ b/tests/coverage/app/Exceptions/Api/V2/dashboard.html @@ -0,0 +1,291 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Exceptions/Api/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + +
ClassCoverage
App\Exceptions\Api\V2\ApiErrorException80%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + +
ClassCRAP
App\Exceptions\Api\V2\ApiErrorException5
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
getApiError0%
report0%
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Exceptions/Api/V2/index.html b/tests/coverage/app/Exceptions/Api/V2/index.html new file mode 100644 index 0000000..500d525 --- /dev/null +++ b/tests/coverage/app/Exceptions/Api/V2/index.html @@ -0,0 +1,135 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Exceptions/Api/V2 + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ApiErrorException.php
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CrmException.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Exceptions/Api/dashboard.html b/tests/coverage/app/Exceptions/Api/dashboard.html new file mode 100644 index 0000000..0fc6555 --- /dev/null +++ b/tests/coverage/app/Exceptions/Api/dashboard.html @@ -0,0 +1,290 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Exceptions/Api + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + +
ClassCoverage
App\Exceptions\Api\V2\ApiErrorException80%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + +
ClassCRAP
App\Exceptions\Api\V2\ApiErrorException5
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
getApiError0%
report0%
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Exceptions/Api/index.html b/tests/coverage/app/Exceptions/Api/index.html new file mode 100644 index 0000000..4c98f08 --- /dev/null +++ b/tests/coverage/app/Exceptions/Api/index.html @@ -0,0 +1,121 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Exceptions/Api + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
V2
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/tests/coverage/app/Exceptions/Handler.php.html b/tests/coverage/app/Exceptions/Handler.php.html new file mode 100644 index 0000000..d387f17 --- /dev/null +++ b/tests/coverage/app/Exceptions/Handler.php.html @@ -0,0 +1,387 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Exceptions/Handler.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
CRAP
+
+ 90.00% covered (success) +
+
+
90.00%
36 / 40
Handler
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
14.20
+
+ 90.00% covered (success) +
+
+
90.00%
36 / 40
 register
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 render
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 handleApiV2Exception
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
9.14
+
+ 87.88% covered (warning) +
+
+
87.88%
29 / 33
 getReponse
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Exceptions;
4
5use App\Enums\ApiError;
6use App\Enums\ApiStatus;
7use App\Exceptions\Api\V2\ApiErrorException;
8use App\Http\Resources\Api\V2\ApiResponse as V2ApiResponse;
9use App\Lib\Helper;
10use Illuminate\Auth\Access\AuthorizationException;
11use Illuminate\Auth\AuthenticationException;
12use Illuminate\Database\Eloquent\ModelNotFoundException;
13use Illuminate\Http\Exceptions\ThrottleRequestsException;
14use Illuminate\Http\JsonResponse;
15use Illuminate\Http\Request;
16use Illuminate\Validation\ValidationException;
17use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;
18use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
19use Throwable;
20
21class Handler extends ExceptionHandler
22{
23    /**
24     * A list of the exception types that are not reported.
25     *
26     * @var array
27     */
28    protected $dontReport = [
29        ApiErrorException::class,
30        RedirectTo::class
31    ];
32
33    /**
34     * A list of the inputs that are never flashed for validation exceptions.
35     *
36     * @var array
37     */
38    protected $dontFlash = [
39        'password',
40        'password_confirmation',
41    ];
42
43    /**
44     * Register the exception handling callbacks for the application.
45     *
46     * @return void
47     */
48    public function register()
49    {
50    }
51
52    /**
53     * Render an exception into an HTTP response.
54     *
55     * @param  mixed      $request
56     * @param  \Throwable $e
57     * @return \Symfony\Component\HttpFoundation\Response
58     */
59    public function render($request, Throwable $e)
60    {
61        if (Helper::isRequestForApiV2($request) and $request->isJson()) {
62            return $this->handleApiV2Exception($request, $e);
63        } //end if
64        // @codeCoverageIgnoreStart
65        return parent::render($request, $e);
66        // @codeCoverageIgnoreEnd
67    }
68
69    /**
70     * Generate a json response for any apiV2 error case
71     *
72     * @param Request    $request
73     * @param \Throwable $e
74     * @return JsonResponse
75     */
76    private function handleApiV2Exception(Request $request, \Throwable $e): JsonResponse
77    {
78        switch (get_class($e)) {
79            case AuthenticationException::class:
80                /** @var AuthenticationException $e */
81                return $this->getReponse(
82                    $request,
83                    401,
84                    ApiError::apiAuthFailed()->value,
85                    sprintf('%s, %s', ApiError::apiAuthFailed()->label, $e->getMessage())
86                );
87            case MethodNotAllowedHttpException::class:
88                /** @var MethodNotAllowedHttpException $e */
89                return $this->getReponse(
90                    $request,
91                    $e->getStatusCode(),
92                    ApiError::methodNotAllowed()->value,
93                    sprintf('%s, %s', ApiError::methodNotAllowed()->label, $e->getMessage())
94                );
95            case ValidationException::class:
96                /** @var ValidationException $e */
97                return $this->getReponse(
98                    $request,
99                    $e->status,
100                    ApiError::validationFailed()->value,
101                    sprintf(ApiError::validationFailed()->label, $e->getMessage()),
102                    ['errors' => $e->errors()]
103                );
104            case ModelNotFoundException::class:
105                /** @var ModelNotFoundException $e */
106                return $this->getReponse(
107                    $request,
108                    404,
109                    ApiError::modelNotFound()->value,
110                    sprintf(
111                        ApiError::modelNotFound()->label,
112                        \array_slice(explode('\\', $e->getModel()), -1)[0]
113                    )
114                );
115            case ApiErrorException::class:
116                /** @var ApiErrorException $e */
117                return $this->getReponse(
118                    $request,
119                    $e->getStatusCode(),
120                    $e->getCode(),
121                    $e->getMessage()
122                );
123            case ThrottleRequestsException::class:
124                /** @var ThrottleRequestsException $e */
125                return $this->getReponse(
126                    $request,
127                    429,
128                    ApiError::throttleRequestsException()->value,
129                    ApiError::throttleRequestsException()->label
130                );
131            case AuthorizationException::class:
132                /** @var AuthorizationException $e */
133                return $this->getReponse(
134                    $request,
135                    403,
136                    ApiError::authorizationException()->value,
137                    ApiError::authorizationException()->label
138                );
139        } //end switch
140        // @codeCoverageIgnoreStart
141        if (config('app.env') === 'testing') {
142            // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Found
143            print_r((string)$e);
144        }
145        return $this->getReponse($request, 500, $e->getCode(), $e->getMessage());
146        // @codeCoverageIgnoreEnd
147    }
148
149    /**
150     * Generate an ApiV2 json response
151     *
152     * @param Request $request
153     * @param integer $httpCode
154     * @param integer $apiCode
155     * @param string  $message
156     * @param array   $data
157     * @return JsonResponse
158     */
159    private static function getReponse(
160        Request $request,
161        int $httpCode,
162        int $apiCode,
163        string $message,
164        array $data = []
165    ): JsonResponse {
166        return (new V2ApiResponse(
167            (object)array_merge($data, ['message' => $message, 'code' => $apiCode]),
168            ApiStatus::KO()
169        ))->toResponse($request)->setStatusCode($httpCode);
170    }
171}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Exceptions/dashboard.html b/tests/coverage/app/Exceptions/dashboard.html new file mode 100644 index 0000000..b28d952 --- /dev/null +++ b/tests/coverage/app/Exceptions/dashboard.html @@ -0,0 +1,291 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Exceptions + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + +
ClassCoverage
App\Exceptions\Api\V2\ApiErrorException80%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + +
ClassCRAP
App\Exceptions\Api\V2\ApiErrorException5
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + +
MethodCoverage
getApiError0%
report0%
handleApiV2Exception87%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + +
MethodCRAP
handleApiV2Exception9
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Exceptions/index.html b/tests/coverage/app/Exceptions/index.html new file mode 100644 index 0000000..bef91ba --- /dev/null +++ b/tests/coverage/app/Exceptions/index.html @@ -0,0 +1,161 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Exceptions + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 88.00% covered (warning) +
+
+
88.00%
44 / 50
+
+ 62.50% covered (warning) +
+
+
62.50%
5 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
Api
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbortRowImportException.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Handler.php
+
+ 90.00% covered (success) +
+
+
90.00%
36 / 40
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/tests/coverage/app/Http/Controllers/AnswerController.php.html b/tests/coverage/app/Http/Controllers/AnswerController.php.html new file mode 100644 index 0000000..dbc61fd --- /dev/null +++ b/tests/coverage/app/Http/Controllers/AnswerController.php.html @@ -0,0 +1,187 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/AnswerController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AnswerController
n/a
0 / 0
n/a
0 / 0
4
n/a
0 / 0
 indexQuery
n/a
0 / 0
2
n/a
0 / 0
 index
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Http\Controllers\Controller;
6use App\Models\Answer;
7use Illuminate\Database\Eloquent\Builder;
8use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
9use Kwaadpepper\CrudPolicies\Traits\CrudController;
10
11/**
12 * @codeCoverageIgnore
13 */
14class AnswerController extends Controller
15{
16    use CrudController {
17        index as crudIndex;
18    }
19
20    /** @var Log */
21    public static $modelClass = Answer::class;
22
23    /**
24     * The Bo app layout
25     *
26     * @var string
27     */
28    public static $layout = 'layouts.app';
29
30    /**
31     * Event index crud-policies
32     *
33     * @param Builder $query
34     * @return Builder
35     */
36    public function indexQuery(Builder $query): Builder
37    {
38        $parent = request()->question;
39        if ($parent) {
40            $query->whereHas('question', function ($q) use ($parent) {
41                return $q->whereId($parent->id);
42            });
43        }
44        return $query;
45    }
46
47    /**
48     * Overload crud-policies index
49     *
50     * @param CrudIndexRequest $request
51     * @return \Illuminate\Http\Response
52     */
53    public function index(CrudIndexRequest $request)
54    {
55        /** @var \Illuminate\View\View */
56        $view = $this->crudIndex($request);
57        if ($request->ajax()) {
58            return $view;
59        }
60        return view('answer.index', $view->getData());
61    }
62}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V1/ArcadiaController.php.html b/tests/coverage/app/Http/Controllers/Api/V1/ArcadiaController.php.html new file mode 100644 index 0000000..23c2585 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V1/ArcadiaController.php.html @@ -0,0 +1,512 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V1/ArcadiaController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ArcadiaController
n/a
0 / 0
n/a
0 / 0
57
n/a
0 / 0
 outgoingMail
n/a
0 / 0
10
n/a
0 / 0
 checkShippingRequiredFields
n/a
0 / 0
4
n/a
0 / 0
 checkRequiredFields
n/a
0 / 0
21
n/a
0 / 0
 makeXml
n/a
0 / 0
22
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V1;
4
5use App\Http\Controllers\Controller;
6use App\Http\Resources\StatusResource;
7use App\Mail\CourrierArcadiaError;
8use App\Models\Customer;
9use DOMDocument;
10use Illuminate\Http\Request;
11use Illuminate\Support\Facades\Log;
12use Illuminate\Support\Facades\Mail;
13use Illuminate\Support\Facades\Storage;
14use Illuminate\Support\Facades\Validator;
15
16/**
17 * @codeCoverageIgnore
18 */
19class ArcadiaController extends Controller
20{
21    /**
22     * @param Request $request
23     * @return StatusResource
24     */
25    public function outgoingMail(Request $request)
26    {
27        $validator = Validator::make($request->all(), [
28            'crm_id' => 'required',
29            'fields' => 'required|json',
30            'type' => 'required',
31            'shipping' => 'required_if:type,default|json'
32        ]);
33
34        if ($validator->fails()) {
35            return new StatusResource([
36                'status' => 'KO',
37                'code' => 201,
38                'message' => 'Error on required fields.',
39                'errors' => $validator->errors()
40            ]);
41        }
42
43        $brandId  = $request->brand_id;
44        $crmId    = $request->crm_id;
45        $fields   = json_decode($request->fields);
46        $type     = $request->type;
47        $shipping = $type === 'default' ? json_decode($request->shipping) : false;
48
49        $customer = Customer::where('id', $crmId)->first();
50        if (!$customer) {
51            return new StatusResource(['status' => 'KO', 'code' => 202, 'message' => 'User not found.']);
52        }
53
54        $CustomerInfo = $customer->infos()->where('brand_id', $brandId)->first();
55
56        if (!$CustomerInfo) {
57            return new StatusResource(['status' => 'KO', 'code' => 202, 'message' => 'User not found.']);
58        }
59
60        $check = $this->checkRequiredFields($fields, $type);
61        if ($check['status'] !== 'OK') {
62            return new StatusResource($check);
63        }
64
65        if ($type === 'default') {
66            $check = $this->checkShippingRequiredFields($shipping);
67            if ($check['status'] !== 'OK') {
68                return new StatusResource($check);
69            }
70        }
71
72        $xml = $this->makeXml($fields, $type, $customer, $CustomerInfo, $shipping);
73
74        try {
75            Storage::disk('arcadia')->put(date("Ymd_His") . microtime(true) . ".xml", $xml);
76            return new StatusResource(['status' => 'OK']);
77        } catch (\Exception $e) {
78            Log::critical("ARCADIA XML CREATION FAILED : " . $e->getMessage());
79            Mail::to(config('mail.MAIL_ADMIN'))->send(new CourrierArcadiaError(
80                "ARCADIA XML CREATION FAILED",
81                "Une erreur est survenue lors de la création du fichier XML : " . $e->getMessage(),
82                $xml ?: ''
83            ));
84            return new StatusResource(['status' => 'KO', 'code' => 204, 'message' => 'Arcadia XML Creation failed.']);
85        }
86    }
87
88    /**
89     * @param mixed $fields
90     * @return array
91     */
92    private function checkShippingRequiredFields($fields)
93    {
94        $test = [
95            'firstname',
96            'lastname',
97            'address',
98            'city',
99            'postcode'
100        ];
101
102        foreach ($test as $t) {
103            if (!array_key_exists($t, $fields)) {
104                $errors[] = ['field' => $t, 'error' => 'required'];
105            }
106        }
107
108        if (!empty($errors)) {
109            return ['status' => 'KO', 'code' => 203, 'message' => $errors];
110        }
111
112        return ['status' => 'OK'];
113    }
114
115    /**
116     *
117     * @param mixed $fields
118     * @param mixed $type
119     * @return array
120     */
121    // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
122    private function checkRequiredFields($fields, $type): array
123    {
124        $errors = [];
125
126        $key = array_search('operation', array_column($fields->root, 'fieldName'));
127        if ($key === false) {
128            $errors[] = ['field' => 'operation', 'error' => 'required'];
129        }
130
131        $key = array_search('support_code', array_column($fields->orderLine, 'fieldName'));
132        if ($key === false) {
133            $errors[] = ['field' => 'support_code', 'error' => 'required'];
134        }
135
136        $key = array_search('label_type_support', array_column($fields->orderLine, 'fieldName'));
137        if ($key === false) {
138            $errors[] = ['field' => 'label_type_support', 'error' => 'required'];
139        }
140
141        $key = array_search('order_date', array_column($fields->root, 'fieldName'));
142        if ($key === false) {
143            $errors[] = ['field' => 'order_date', 'error' => 'required'];
144        }
145
146        $regex = join('', [
147            '/^(((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((19|[2-9]\d)\d{2}))|',
148            '((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((19|[2-9]\d)\d{2}))',
149            '|((0[1-9]|1\d|2[0-8])\/02\/((19|[2-9]\d)\d{2}))|(29\/02\/((1[6-9]|[2-9]\d)(0[48]',
150            '|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/'
151        ]);
152        if (!preg_match($regex, $fields->root[$key]->fieldValue)) {
153            $errors[] = ['field' => 'order_date', 'error' => 'format must be DD/MM/YYYY'];
154        }
155
156        if ($type === 'default') {
157            $key = array_search('order_num', array_column($fields->root, 'fieldName'));
158            if ($key === false) {
159                $errors[] = ['field' => 'order_num', 'error' => 'required for default type'];
160            }
161
162            if (!is_numeric($fields->root[$key]->fieldValue) || strlen($fields->root[$key]->fieldValue) !== 9) {
163                $errors[] = ['field' => 'order_num', 'error' => 'format must be 9 numeric characters'];
164            }
165
166            $key = array_search('state_order_cb', array_column($fields->root, 'fieldName'));
167            if ($key === false) {
168                $errors[] = ['field' => 'state_order_cb', 'error' => 'required for default type'];
169            }
170
171            if ($fields->root[$key]->fieldValue !== 1 && $fields->root[$key]->fieldValue !== 'Null') {
172                $errors[] = ['field' => 'state_order_cb', 'error' => 'value must be 1 or Null (Yes Null not NULL)'];
173            }
174
175            $orderCB = $fields->root[$key]->fieldValue;
176
177            $key = array_search('state_order_check', array_column($fields->root, 'fieldName'));
178            if ($key === false) {
179                $errors[] = ['field' => 'state_order_check', 'error' => 'required for default type'];
180            }
181
182            if ($fields->root[$key]->fieldValue !== 1 && $fields->root[$key]->fieldValue !== 'Null') {
183                $errors[] = ['field' => 'state_order_check', 'error' => 'value must be 1 or Null (Yes Null not NULL)'];
184            }
185
186            $orderCheck = $fields->root[$key]->fieldValue;
187
188            if ($orderCB === $orderCheck) {
189                $errors[] = [
190                    'field' => 'state_order',
191                    'error' => 'state_order_cb and state_order_check values must be different'
192                ];
193            }
194
195            $key = array_search('package_unit_weight', array_column($fields->orderLine, 'fieldName'));
196            if ($key === false) {
197                $errors[] = ['field' => 'package_unit_weight', 'error' => 'required for default type'];
198            }
199        } elseif ($type === 'game') {
200            $key = array_search('code_unique', array_column($fields->orderLine, 'fieldName'));
201            if ($key === false) {
202                $errors[] = ['field' => 'code_unique', 'error' => 'required for game type'];
203            }
204        }//end if
205
206        if (!empty($errors)) {
207            return ['status' => 'KO', 'code' => 203, 'message' => $errors];
208        }
209
210        return ['status' => 'OK'];
211    }
212
213    /**
214     * @param mixed $fields
215     * @param mixed $type
216     * @param mixed $customer
217     * @param mixed $customerInfos
218     * @param mixed $shipping
219     * @return string|false
220     */
221    // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
222    private function makeXml($fields, $type, $customer, $customerInfos, $shipping)
223    {
224        $pivot = [
225            'order_num' => 'Num_Commande',
226            // Required if type = default - format 9 numeric characters.
227            'order_date' => 'Date_Commande',
228            // Required - format : DD/MM/YYYY .
229            'operation' => 'Operation',
230            // Required.
231            'support_code' => 'Code_Support',
232            // Required.
233            'label_type_support' => 'Libelle_Type_Support',
234            // Required.
235            'package_unit_weight' => 'Poids_Unitaire_Colis',
236            // Required if type = default.
237            'qty' => 'Quantite',
238            'code_on_pack' => 'Code_on_Pack',
239            'code_unique' => 'Code_Unique',
240            // Required if type = game.
241            // Required if type = default - format 1 ou Null (oui Null et pas NULL).
242            'state_order_cb' => 'Etat_Commande_CB',
243            // Required if type = default - format 1 ou Null (oui Null et pas NULL).
244            'state_order_check' => 'Etat_Commande_Cheque',
245
246        ];
247
248        $dom               = new DOMDocument('1.0', 'UTF-8');
249        $dom->formatOutput = true;
250
251        $root = $dom->createElement('export_arcadia');
252        $dom->appendChild($root);
253        $order = $root->appendChild($dom->createElement('Commande'));
254
255        $key = array_search('order_num', array_column($fields->root, 'fieldName'));
256        if ($key === false) {
257            $order->appendChild($dom->createElement('Num_Commande'));
258        } else {
259            $order->appendChild($dom->createElement('Num_Commande', $fields->root[$key]->fieldValue));
260        }
261
262        foreach ($fields->root as $value) {
263            // phpcs:ignore PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace
264            if (
265                isset($pivot[$value->fieldName]) && $value->fieldName !== 'state_order_cb' &&
266                $value->fieldName !== 'state_order_check' && $value->fieldName !== 'order_num'
267            ) {
268                $order->appendChild($dom->createElement($pivot[$value->fieldName], $value->fieldValue));
269            }
270        }
271
272        if ($type === 'default') {
273            $key = array_search('state_order_cb', array_column($fields->root, 'fieldName'));
274            if ($key) {
275                $order->appendChild($dom->createElement('Etat_Commande_CB', $fields->root[$key]->fieldValue));
276            }
277
278            $key = array_search('state_order_check', array_column($fields->root, 'fieldName'));
279            if ($key) {
280                $order->appendChild($dom->createElement('Etat_Commande_Cheque', $fields->root[$key]->fieldValue));
281            }
282        }
283
284        $order->appendChild($dom->createElement('Email_conso'))
285        ->appendChild($dom->createCDATASection("$customer->email"));
286        $order->appendChild($dom->createElement('Civilite'));
287
288        if ($type === 'default') {
289            $order->appendChild($dom->createElement('Nom'))
290                ->appendChild($dom->createCDATASection("$shipping->lastname"));
291            $order->appendChild($dom->createElement('Prenom'))
292                ->appendChild($dom->createCDATASection("$shipping->firstname"));
293            $order->appendChild($dom->createElement('Adresse'))
294                ->appendChild($dom->createCDATASection("$shipping->address"));
295            $order->appendChild($dom->createElement('Adresse_Complementaire'));
296            $order->appendChild($dom->createElement('CP'))
297            ->appendChild($dom->createCDATASection("$shipping->postcode"));
298            $order->appendChild($dom->createElement('Ville'))->appendChild($dom->createCDATASection("$shipping->city"));
299            if (isset($shipping->phone) && !empty($shipping->phone)) {
300                $order->appendChild($dom->createElement('Telephone'))
301                    ->appendChild($dom->createCDATASection("$shipping->phone"));
302            } else {
303                $order->appendChild($dom->createElement('Telephone'));
304            }
305        } else {
306            $order->appendChild($dom->createElement('Nom'))
307                ->appendChild($dom->createCDATASection("$customerInfos->lastname"));
308            $order->appendChild($dom->createElement('Prenom'))
309                ->appendChild($dom->createCDATASection("$customerInfos->firstname"));
310            $order->appendChild($dom->createElement('Adresse'))
311                ->appendChild($dom->createCDATASection("$customerInfos->address"));
312            $order->appendChild($dom->createElement('Adresse_Complementaire'))
313                ->appendChild($dom->createCDATASection("$customerInfos->address2"));
314            $order->appendChild($dom->createElement('CP'))
315                ->appendChild($dom->createCDATASection("$customerInfos->postcode"));
316            $order->appendChild($dom->createElement('Ville'))
317                ->appendChild($dom->createCDATASection("$customerInfos->city"));
318            $order->appendChild($dom->createElement('Telephone'))
319                ->appendChild($dom->createCDATASection("$customerInfos->phone"));
320        }//end if
321
322        $orderLine = $order->appendChild($dom->createElement('Ligne_de_Commande'));
323
324        $key = array_search('qty', array_column($fields->orderLine, 'fieldName'));
325        if ($key === false) {
326            $orderLine->appendChild($dom->createElement('Quantite', '1'));
327        }
328
329        $key = array_search('package_unit_weight', array_column($fields->orderLine, 'fieldName'));
330        if ($key === false) {
331            $orderLine->appendChild($dom->createElement('Poids_Unitaire_Colis'));
332        }
333
334        foreach ($fields->orderLine as $value) {
335            // phpcs:ignore PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace
336            if (
337                isset($pivot[$value->fieldName]) &&
338                $value->fieldName !== 'code_on_pack' &&
339                $value->fieldName !== 'code_unique'
340            ) {
341                $orderLine->appendChild($dom->createElement($pivot[$value->fieldName], $value->fieldValue));
342            }
343        }
344
345        if ($type === 'game') {
346            $key = array_search('code_on_pack', array_column($fields->orderLine, 'fieldName'));
347            if ($key) {
348                $orderLine->appendChild(
349                    $dom->createElement('Code_on_Pack')
350                )->appendChild($dom->createCDATASection($fields->orderLine[$key]->fieldValue));
351            }
352
353            $key = array_search('code_unique', array_column($fields->orderLine, 'fieldName'));
354            if ($key) {
355                $orderLine->appendChild(
356                    $dom->createElement('Code_Unique')
357                )->appendChild($dom->createCDATASection($fields->orderLine[$key]->fieldValue));
358            }
359        }
360
361        return $dom->saveXML();
362    }
363}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V1/CustomerController.php.html b/tests/coverage/app/Http/Controllers/Api/V1/CustomerController.php.html new file mode 100644 index 0000000..70b79c4 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V1/CustomerController.php.html @@ -0,0 +1,785 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V1/CustomerController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerController
n/a
0 / 0
n/a
0 / 0
69
n/a
0 / 0
 store
n/a
0 / 0
12
n/a
0 / 0
 show
n/a
0 / 0
6
n/a
0 / 0
 update
n/a
0 / 0
22
n/a
0 / 0
 authenticate
n/a
0 / 0
6
n/a
0 / 0
 changePassword
n/a
0 / 0
5
n/a
0 / 0
 activateAccount
n/a
0 / 0
4
n/a
0 / 0
 delete
n/a
0 / 0
4
n/a
0 / 0
 toggleOptin
n/a
0 / 0
4
n/a
0 / 0
 removeOptin
n/a
0 / 0
2
n/a
0 / 0
 addSingleOptin
n/a
0 / 0
4
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V1;
4
5use App\Enums\Country;
6use App\Http\Controllers\Controller;
7use App\Http\Resources\CustomerResource;
8use App\Http\Resources\StatusResource;
9use App\Models\Customer;
10use App\Models\CustomerInfo;
11use App\Models\CustomerLogin;
12use App\Models\CustomerOptInHistory;
13use App\Models\SingleOptin;
14use Illuminate\Http\Request;
15use Illuminate\Support\Facades\Hash;
16use Illuminate\Support\Facades\Validator;
17use Illuminate\Support\Str;
18
19/**
20 * @codeCoverageIgnore
21 */
22class CustomerController extends Controller
23{
24    /**
25     * Store a newly created resource in storage
26     * @param Request $request
27     * @return CustomerResource|StatusResource
28     */
29    // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
30    public function store(Request $request)
31    {
32        $validator = Validator::make($request->all(), [
33            'email' => 'required|email',
34            'password' => 'required',
35            'firstname' => 'required',
36            'lastname' => 'required',
37            'opt_in' => 'required',
38            'birth_date' => 'date_format:Y-m-d'
39        ]);
40
41        if ($validator->fails()) {
42            return new StatusResource([
43                'status' => 'KO', 'code' => 102,
44                'message' => 'Error on required fields.', 'errors' => $validator->errors()
45            ]);
46        }
47
48        $brandId   = $request->brand_id;
49        $email     = strtolower(filter_var($request->email, FILTER_SANITIZE_EMAIL));
50        $firstname = filter_var($request->firstname, FILTER_SANITIZE_STRING);
51        $lastname  = filter_var($request->lastname, FILTER_SANITIZE_STRING);
52        $optIn     = filter_var($request->opt_in, FILTER_SANITIZE_STRING);
53        $hashcode  = Str::uuid();
54
55        // Récupère le customer s'il existe déjà.
56        $customer = Customer::where('email', $email)->first();
57
58        // Si le customer n'existe pas déjà.
59        if (!$customer) {
60            $customer        = new Customer();
61            $customer->email = $email;
62            $customer->save();
63            $singleOptin = SingleOptin::where([['email', $email], ['brand_id', $brandId]])->first();
64            if ($singleOptin) {
65                $singleOptin->delete();
66            }
67        } else {
68            // Récupère les infos s'ils existent déjà.
69            $CustomerInfo = CustomerInfo::where([['customer_id', $customer->id], ['brand_id', $brandId]])->first();
70            if ($CustomerInfo) {
71                return new StatusResource([
72                    'status' => 'KO', 'code' => 103,
73                    'message' => 'User already registered on this site.'
74                ]);
75            }
76        }
77
78        $customerInfos             = new CustomerInfo();
79        $customerInfos->firstname  = ucfirst(strtolower($firstname));
80        $customerInfos->lastname   = ucfirst(strtolower($lastname));
81        $customerInfos->birth_date = !empty($request->birth_date) ?
82        filter_var($request->birth_date, FILTER_SANITIZE_STRING) : null;
83        $customerInfos->address    = !empty($request->address) ?
84        filter_var($request->address, FILTER_SANITIZE_STRING) : null;
85        $customerInfos->address2   = !empty($request->address2) ?
86        filter_var($request->address2, FILTER_SANITIZE_STRING) : null;
87        $customerInfos->phone      = !empty($request->phone) ?
88        filter_var($request->phone, FILTER_SANITIZE_STRING) : null;
89        try {
90            $customerInfos->country = Country::make(filter_var($request->country, FILTER_SANITIZE_STRING));
91        } catch (\Exception $e) {
92            $customerInfos->country = Country::france();
93        }
94        $customerInfos->city        = !empty($request->city) ?
95        filter_var($request->city, FILTER_SANITIZE_STRING) : null;
96        $customerInfos->postcode    = !empty($request->postcode) ?
97        filter_var($request->postcode, FILTER_SANITIZE_STRING) : null;
98        $customerInfos->opt_in      = $optIn;
99        $customerInfos->hashcode    = $hashcode;
100        $customerInfos->brand_id    = $brandId;
101        $customerInfos->customer_id = $customer->id;
102
103        $customer->infos()->save($customerInfos);
104
105        // Création du login pour cet utilisateur sur ce service.
106        $customerLogin                  = new CustomerLogin();
107        $customerLogin->customer_id     = $customer->id;
108        $customerLogin->password        = Hash::make(trim($request->password));
109        $customerLogin->last_connection = now();
110        $customerLogin->brand_id        = $brandId;
111        $customerLogin->status          = 0;
112
113        $customer->logins()->save($customerLogin);
114
115        // Ajout dans la table d'historique des opt-in de la nouvelle valeur.
116        $optInHistory              = new CustomerOptInHistory();
117        $optInHistory->customer_id = $customer->id;
118        $optInHistory->brand_id    = $brandId;
119        $optInHistory->new_value   = $optIn;
120        $optInHistory->save();
121
122        return new CustomerResource($customer, $brandId);
123    }
124
125    /**
126     * Display the specified resource.
127     *
128     * @param  Illuminate\Http\Request $request
129     * @param  mixed                   $type
130     * @param  mixed                   $value
131     * @return CustomerResource | StatusResource
132     */
133    public function show(Request $request, $type, $value)
134    {
135        $brandId = $request->brand_id;
136
137        if ($type === 'id') {
138            $customer = Customer::find($value);
139            if (!$customer) {
140                return new StatusResource(['status' => 'KO', 'code' => 101, 'message' => 'User not found.']);
141            }
142        } elseif ($type === 'email') {
143            $customer = Customer::where('email', $value)->first();
144            if (!$customer) {
145                return new StatusResource(['status' => 'KO', 'code' => 101, 'message' => 'User not found.']);
146            }
147
148            $CustomerInfo = $customer->infos()->where('brand_id', $brandId)->first();
149
150            if (!$CustomerInfo) {
151                return new StatusResource(['status' => 'KO', 'code' => 101, 'message' => 'User not found.']);
152            }
153        } else {
154            return new StatusResource(['status' => 'KO', 'code' => 101, 'message' => 'User not found.']);
155        }
156
157        return new CustomerResource($customer, $brandId);
158    }
159
160    /**
161     * Update the specified resource in storage.
162     *
163     * @param  \Illuminate\Http\Request  $request
164     * @param  int  $id
165     * @return CustomerResource|StatusResource
166     */
167    // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded, Squiz.Commenting.FunctionComment.Missing
168    public function update(Request $request, $id)
169    {
170        $validator = Validator::make($request->all(), [
171            'birth_date' => 'date_format:Y-m-d'
172        ]);
173
174        if ($validator->fails()) {
175            return new StatusResource([
176                'status' => 'KO', 'code' => 104,
177                'message' => 'Error on required fields.', 'errors' => $validator->errors()
178            ]);
179        }
180
181        $brandId  = $request->brand_id;
182        $customer = Customer::find($id);
183
184        if (!$customer) {
185            return new StatusResource(['status' => 'KO', 'code' => 105, 'message' => 'User not found.']);
186        }
187
188        $CustomerInfo = $customer->infos()->where('brand_id', $brandId)->first();
189        if (!$CustomerInfo) {
190            return new StatusResource(['status' => 'KO', 'code' => 105, 'message' => 'User not found.']);
191        }
192
193        if ($request->has('firstname')) {
194            $CustomerInfo->firstname = filter_var($request->firstname, FILTER_SANITIZE_STRING);
195        }
196
197        if ($request->has('lastname')) {
198            $CustomerInfo->lastname = filter_var($request->lastname, FILTER_SANITIZE_STRING);
199        }
200
201        if ($request->has('birth_date')) {
202            if (!empty($request->birth_date)) {
203                $CustomerInfo->birth_date = filter_var($request->birth_date, FILTER_SANITIZE_STRING);
204            } else {
205                $CustomerInfo->birth_date = null;
206            }
207        }
208
209        if ($request->has('address')) {
210            if (!empty($request->address)) {
211                $CustomerInfo->address = filter_var($request->address, FILTER_SANITIZE_STRING);
212            } else {
213                $CustomerInfo->address = null;
214            }
215        }
216
217        if ($request->has('address2')) {
218            if (!empty($request->address2)) {
219                $CustomerInfo->address2 = filter_var($request->address2, FILTER_SANITIZE_STRING);
220            } else {
221                $CustomerInfo->address2 = null;
222            }
223        }
224
225        if ($request->has('phone')) {
226            if (!empty($request->phone)) {
227                $CustomerInfo->phone = filter_var($request->phone, FILTER_SANITIZE_STRING);
228            } else {
229                $CustomerInfo->phone = null;
230            }
231        }
232
233        if ($request->has('country')) {
234            try {
235                $CustomerInfo->country = Country::make(filter_var($request->country, FILTER_SANITIZE_STRING));
236            } catch (\Exception $e) {
237                $CustomerInfo->country = Country::france();
238            }
239        }
240
241        if ($request->has('city')) {
242            if (!empty($request->city)) {
243                $CustomerInfo->city = filter_var($request->city, FILTER_SANITIZE_STRING);
244            } else {
245                $CustomerInfo->city = null;
246            }
247        }
248
249        if ($request->has('postcode')) {
250            if (!empty($request->postcode)) {
251                $CustomerInfo->postcode = filter_var($request->postcode, FILTER_SANITIZE_STRING);
252            } else {
253                $CustomerInfo->postcode = null;
254            }
255        }
256
257        if ($request->has('opt_in')) {
258            $optIn = filter_var($request->opt_in, FILTER_SANITIZE_STRING);
259
260            if ($CustomerInfo->opt_in != $optIn) {
261                // Ajout dans la table d'historique des opt-in de la nouvelle valeur.
262                $optInHistory              = new CustomerOptInHistory();
263                $optInHistory->customer_id = $customer->id;
264                $optInHistory->brand_id    = $brandId;
265                $optInHistory->new_value   = $optIn;
266                $optInHistory->save();
267            }
268
269            $CustomerInfo->opt_in = $optIn;
270        }
271
272        $CustomerInfo->save();
273
274        return new CustomerResource($customer, $brandId);
275    }
276
277    /**
278     * Authenticate the user
279     * @param Request $request
280     * @return CustomerResource|StatusResource
281     */
282    public function authenticate(Request $request)
283    {
284        $validator = Validator::make($request->all(), [
285            'email' => 'required|email',
286            'password' => 'required',
287        ]);
288
289        if ($validator->fails()) {
290            return new StatusResource([
291                'status' => 'KO', 'code' => 115,
292                'message' => 'Error on required fields.', 'errors' => $validator->errors()
293            ]);
294        }
295
296        $brandId  = $request->brand_id;
297        $email    = filter_var($request->email, FILTER_SANITIZE_EMAIL);
298        $password = trim($request->password);
299        $customer = Customer::where('email', $email)->first();
300        if ($customer) {
301            $login = $customer->logins()->where('brand_id', $brandId)->first();
302            if ($login) {
303                if (Hash::check($password, $login->password)) {
304                    if ($login->status === false) {
305                        $infos = $customer->infos()->where('brand_id', $brandId)->first();
306                        // "Le compte de l'utilisateur n'est pas activé"
307                        return new StatusResource(
308                            [
309                                'status' => 'KO',
310                                'code' => 116,
311                                'message' => 'Account not activated.',
312                                'user' => [
313                                    'crm_id' => $customer->id,
314                                    'firstname' => $infos->firstname,
315                                    'lastname' => $infos->lastname,
316                                    'email' => $customer->email,
317                                    'birth_date' => $infos->birth_date,
318                                    'address' => $infos->address,
319                                    'address2' => $infos->address2,
320                                    'phone' => $infos->phone,
321                                    'country' => $infos->country->label,
322                                    'city' => $infos->city,
323                                    'postcode' => $infos->postcode,
324                                    'opt_in' => $infos->opt_in,
325                                    'hashcode' => $infos->hashcode,
326                                    'status' => $login->status,
327                                ]
328                            ]
329                        );
330                    }//end if
331
332                    $login->last_connection = now();
333                    $customer->logins()->save($login);
334                    return new CustomerResource($customer, $brandId);
335                }//end if
336                // "Mot de passe incorrect"
337                return new StatusResource(['status' => 'KO', 'code' => 117, 'message' => 'Authentication failed.']);
338            } else {
339                // "Ce compte n'est pas enregistré pour cette marque"
340                return new StatusResource(['status' => 'KO', 'code' => 118, 'message' => 'User not found.']);
341            }//end if
342        } else {
343            // "Ce compte n'existe pas dans la CRM"
344            return new StatusResource(['status' => 'KO', 'code' => 119, 'message' => 'User not found.']);
345        }//end if
346    }
347
348    /**
349     * Change the password of the user for the brand
350     * @param Request $request
351     * @param mixed   $id
352     * @return StatusResource
353     */
354    public function changePassword(Request $request, $id)
355    {
356        $validator = Validator::make($request->all(), [
357            'password' => 'required',
358        ]);
359
360        if ($validator->fails()) {
361            return new StatusResource([
362                'status' => 'KO', 'code' => 107,
363                'message' => 'Error on required fields.', 'errors' => $validator->errors()
364            ]);
365        }
366
367        $brandId  = $request->brand_id;
368        $customer = Customer::find($id);
369        $password = trim($request->password);
370
371        if ($customer) {
372            $customerLogin = $customer->logins()->where('brand_id', $brandId)->first();
373            if ($customerLogin) {
374                // Check if same password.
375                if (Hash::check($password, $customerLogin->password)) {
376                    return new StatusResource(['status' => 'KO', 'code' => 123, 'message' => 'Same password.']);
377                }
378
379                $customerLogin->password = Hash::make($password);
380                $customerLogin->save();
381            } else {
382                // "Ce compte n'est pas enregistré pour cette marque"
383                return new StatusResource(['status' => 'KO', 'code' => 108, 'message' => 'User not found.']);
384            }
385        } else {
386            // "Ce compte n'existe pas"
387            return new StatusResource(['status' => 'KO', 'code' => 109, 'message' => 'User not found.']);
388        }
389        return new StatusResource(['status' => 'OK', 'message' => 'Update complete.']);
390    }
391
392    /**
393     * Activate the account of the user
394     * @param Request $request
395     * @param mixed   $id
396     * @return StatusResource
397     */
398    public function activateAccount(Request $request, $id)
399    {
400        $brandId = $request->brand_id;
401
402        $customer = Customer::find($id);
403        if ($customer) {
404            $login = $customer->logins()->where('brand_id', $brandId)->first();
405            if ($login) {
406                if ($login->status === false) {
407                    $login->status = true;
408                    $login->save();
409                    // "Le compte a bien été activé"
410                    return new StatusResource(['status' => 'OK', 'message' => 'Update complete.']);
411                } else {
412                    // "Ce compte est déjà activé"
413                    return new StatusResource(['status' => 'KO', 'code' => 110, 'message' => 'User already active.']);
414                }
415            } else {
416                // "Ce compte n'est pas enregistré pour cette marque"
417                return new StatusResource(['status' => 'KO', 'code' => 111, 'message' => 'User not found.']);
418            }
419        } else {
420            // "Cet utilisateur n'existe pas dans la CRM"
421            return new StatusResource(['status' => 'KO', 'code' => 112, 'message' => 'User not found.']);
422        }
423    }
424
425    /**
426     * Delete the user account for the brand
427     * @param Request $request
428     * @param mixed   $id
429     */
430    // phpcs:disable Squiz.PHP.NonExecutableCode.Unreachable, Squiz.Commenting.FunctionComment.Missing
431    public function delete(Request $request, $id)
432    {
433        die("Not implemented yet");
434        // TODO : Gestion avec mesBR.
435        $brandId = $request->brand_id;
436
437        $customer = Customer::find($id);
438        if ($customer) {
439            $customer->infos()->where('brand_id', $brandId)->first()->delete();
440            $customer->logins()->where('brand_id', $brandId)->first()->delete();
441
442            if (count($customer->infos) === 0 && count($customer->logins) === 0) {
443                $customer->optInHistory()->delete();
444                $customer->delete();
445                // Suppression du compte de la CRM terminée.
446                return new StatusResource(['status' => 'OK', 'message' => 'Deletion complete.']);
447            } else {
448                // Suppression des informations du compte pour la marque terminée.
449                return new StatusResource(['status' => 'OK', 'message' => 'Deletion complete.']);
450            }
451        } else {
452            return new StatusResource(['status' => 'KO', 'code' => 106, 'message' => 'User not found.']);
453        }
454        // phpcs:enable Squiz.PHP.NonExecutableCode.Unreachable, Squiz.Commenting.FunctionComment.Missing
455    }
456
457    /**
458     * Change the status of the opt-in for the customer on this brand
459     * @param Request $request
460     * @param mixed   $id
461     * @return StatusResource
462     */
463    public function toggleOptin(Request $request, $id)
464    {
465        // TODO : Liaison des opt-in avec MailJet.
466        $brandId = $request->brand_id;
467
468        $customer = Customer::find($id);
469        if ($customer) {
470            $infos = $customer->infos()->where('brand_id', $brandId)->first();
471            if ($infos) {
472                $infos->opt_in = $infos->opt_in === 0 ? 1 : 0;
473
474                // On enregistre l'historique du changement d'optin.
475                $optinHistory              = new CustomerOptInHistory();
476                $optinHistory->customer_id = $customer->id;
477                $optinHistory->new_value   = $infos->opt_in;
478                $optinHistory->brand_id    = $brandId;
479                $optinHistory->save();
480
481                $infos->save();
482                // "Changement effectué"
483                return new StatusResource(['status' => 'OK', 'message' => 'Update complete.']);
484            } else {
485                // "Ce compte n'a pas d'information liés à cette marque"
486                return new StatusResource(['status' => 'KO', 'code' => 113, 'message' => 'User not found.']);
487            }
488        } else {
489            // "Cet utilisateur n'existe pas dans la CRM"
490            return new StatusResource(['status' => 'KO', 'code' => 114, 'message' => 'User not found.']);
491        }//end if
492    }
493
494    /**
495     * Remove opt-in for the customer on this brand
496     * @param Request $request
497     * @param mixed   $hashcode
498     * @return StatusResource
499     */
500    public function removeOptin(Request $request, $hashcode)
501    {
502        // TODO : Liaison des opt-in avec MailJet.
503        $brandId = $request->brand_id;
504
505        $infos = CustomerInfo::where([
506            ['brand_id', $brandId],
507            ['hashcode', $hashcode]
508        ])->first();
509
510        if ($infos) {
511            $infos->opt_in = 0;
512
513            // On enregistre l'historique du changement d'optin.
514            $optinHistory              = new CustomerOptInHistory();
515            $optinHistory->customer_id = $infos->customer_id;
516            $optinHistory->new_value   = 0;
517            $optinHistory->brand_id    = $brandId;
518            $optinHistory->save();
519
520            $infos->save();
521            // "Changement effectué"
522            return new StatusResource(['status' => 'OK', 'message' => 'Update complete.']);
523        } else {
524            // "Ce compte n'a pas d'information liés à cette marque"
525            return new StatusResource(['status' => 'KO', 'code' => 122, 'message' => 'User not found.']);
526        }
527    }
528
529    /**
530     * @param Request $request
531     * @return void
532     */
533    // phpcs:ignore Squiz.PHP.NonExecutableCode.Unreachable, Squiz.Commenting.FunctionComment.Missing
534    public function addSingleOptin(Request $request)
535    {
536        $validator = Validator::make($request->all(), [
537            'email' => 'required|email',
538        ]);
539
540        if ($validator->fails()) {
541            return new StatusResource([
542                'status' => 'KO', 'code' => 120,
543                'message' => 'Error on required fields.', 'errors' => $validator->errors()
544            ]);
545        }
546
547        $brandId = $request->brand_id;
548        $email   = filter_var($request->email, FILTER_SANITIZE_EMAIL);
549
550        // Check si le customer ou l'optin simple existe avant de l'ajouter en optin simple.
551        if (
552            !SingleOptin::where([['email', $email], ['brand_id', $brandId]])->exists() &&
553            !CustomerInfo::join("customers", "customer_infos.customer_id", "=", "customers.id")->where([
554                ['brand_id', $brandId], ['email', $email]
555            ])->exists()
556        ) {
557            $singleOptin           = new SingleOptin();
558            $singleOptin->email    = $email;
559            $singleOptin->brand_id = $brandId;
560            $singleOptin->save();
561        } else {
562            // "Cet utilisateur est déjà inscrit à la newsletter pour cette marque"
563            return new StatusResource([
564                'status' => 'KO', 'code' => 121,
565                'message' => 'This user is already subscribed to the newsletter for this brand.'
566            ]);
567        }
568        return new StatusResource(['status' => 'OK', 'message' => 'Optin added.']);
569    }
570}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V1/dashboard.html b/tests/coverage/app/Http/Controllers/Api/V1/dashboard.html new file mode 100644 index 0000000..d656b66 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V1/dashboard.html @@ -0,0 +1,288 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V1 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V1/index.html b/tests/coverage/app/Http/Controllers/Api/V1/index.html new file mode 100644 index 0000000..00308a7 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V1/index.html @@ -0,0 +1,106 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V1 + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ArcadiaController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
CustomerController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Http/Controllers/Api/V2/AnswerController.php.html b/tests/coverage/app/Http/Controllers/Api/V2/AnswerController.php.html new file mode 100644 index 0000000..4c79750 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V2/AnswerController.php.html @@ -0,0 +1,191 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V2/AnswerController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
AnswerController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
 storeCustomerAnswers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Http\Controllers\Controller;
6use App\Http\Requests\Api\V2\StoreCustomerAnswers;
7use App\Models\Answer;
8use App\Models\Customer;
9use App\Models\Service;
10use Illuminate\Http\JsonResponse;
11
12class AnswerController extends Controller
13{
14    /**
15     * Store the customer answers
16     *
17     * @param  StoreCustomerAnswers $request
18     * @param  Service              $service
19     * @param  Customer             $customer
20     * @return JsonResponse
21     */
22    public function storeCustomerAnswers(
23        StoreCustomerAnswers $request,
24        Service $service,
25        Customer $customer
26    ): JsonResponse {
27        // * Make sure we are accessing a customer that belongs to our service.
28        $this->authorize('customerBelongsToService', $customer);
29
30        $existingAnswerIds = $customer->getAnswers($service)->pluck('id');
31        $answersID         = Answer::whereIn('id', $request->answers)->pluck('id');
32        $customer->answers()->sync($answersID->merge($existingAnswerIds));
33        return response()->json();
34    }
35}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V2/ApiController.php.html b/tests/coverage/app/Http/Controllers/Api/V2/ApiController.php.html new file mode 100644 index 0000000..89e8aad --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V2/ApiController.php.html @@ -0,0 +1,175 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V2/ApiController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 ping
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Http\Controllers\Controller;
6use Illuminate\Http\JsonResponse;
7
8class ApiController extends Controller
9{
10    /**
11     * Just returns HTTP 200 response
12     *
13     * @return JsonResponse
14     */
15    public function ping(): JsonResponse
16    {
17        return response()->json();
18    }
19}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V2/ArcadiaController.php.html b/tests/coverage/app/Http/Controllers/Api/V2/ArcadiaController.php.html new file mode 100644 index 0000000..657d64d --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V2/ArcadiaController.php.html @@ -0,0 +1,358 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V2/ArcadiaController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
31 / 31
ArcadiaController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
8
+
+ 100.00% covered (success) +
+
+
100.00%
31 / 31
 outgoingMailDefault
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 outgoingMailGame
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 handleArcadiaMail
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
21 / 21
 makeXml
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Enums\ArcadiaMailType;
6use App\Enums\ArcadiaOrderType;
7use App\Http\Controllers\Controller;
8use App\Http\Requests\Api\V2\BaseApiRequest;
9use App\Http\Requests\Api\V2\SendArchadiaDefaultMailRequest;
10use App\Http\Requests\Api\V2\SendArchadiaGameMailRequest;
11use App\Mail\CourrierArcadiaError;
12use DOMDocument;
13use Illuminate\Http\JsonResponse;
14use Illuminate\Support\Facades\Log;
15use Illuminate\Support\Facades\Mail;
16use Illuminate\Support\Facades\Storage;
17use Illuminate\Support\Str;
18
19/**
20 * ! Ici c'est du sale à cause d'arcadia
21 * Ils ont fait le choix de rajouter une fonctionnalité
22 * sur une methode existante en rajoutant des conditions
23 * sales. Ils ont donc une seule méthode pour envoyer plusieurs
24 * types de réclas avec des champs différents...
25 *
26 * Donc une séparation est faite sur les endpoints CRM et dans le client
27 * pour éviter une confusion de nos utilisateurs
28 */
29class ArcadiaController extends Controller
30{
31    /**
32     * Send arcadia default mail (reclamations)
33     *
34     * @param SendArchadiaDefaultMailRequest $request
35     * @return JsonResponse
36     */
37    public function outgoingMailDefault(SendArchadiaDefaultMailRequest $request): JsonResponse
38    {
39        return $this->handleArcadiaMail($request, ArcadiaMailType::default());
40    }
41
42    /**
43     * Send arcadia game mail (games)
44     *
45     * @param SendArchadiaGameMailRequest $request
46     * @return JsonResponse
47     */
48    public function outgoingMailGame(SendArchadiaGameMailRequest $request): JsonResponse
49    {
50        return $this->handleArcadiaMail($request, ArcadiaMailType::game());
51    }
52
53    /**
54     * Form xml message and save it in the apropriate storage location
55     * to be picked up by the arcadia job
56     *
57     * @param BaseApiRequest  $request
58     * @param ArcadiaMailType $type
59     * @return JsonResponse
60     * @throws \Exception If an error occurs while generating and saving the xml file.
61     */
62    private function handleArcadiaMail(BaseApiRequest $request, ArcadiaMailType $type): JsonResponse
63    {
64        $request = $request->merge([
65            'order_num' => str_pad(\intval($request->order_num), 9, '0', STR_PAD_LEFT)
66        ]);
67        $xml     = 'none';
68        try {
69            $customerInfo = \array_intersect_key($request->all()['deliver'], [
70                'email' => '',
71                'firstname' => '',
72                'lastname' => '',
73                'address' => '',
74                'address2' => '',
75                'city' => '',
76                'postcode' => '',
77                'phone' => ''
78            ]);
79            $orderType    = ArcadiaOrderType::make(intval($request->state_order));
80            $xml          = $this->makeXml($request, $type, $orderType, $customerInfo);
81
82            // * Format xml.
83            $doc                     = new DOMDocument('1.0', 'utf-8');
84            $doc->xmlStandalone      = true;
85            $doc->preserveWhiteSpace = false;
86            $doc->formatOutput       = true;
87            $doc->loadXML($xml);
88            $xml = $doc->saveXML();
89
90            Storage::disk('arcadia')->put(Str::slug(sprintf(
91                'v2_order_%s_%s_%s%s',
92                $request->order_num,
93                $type->label,
94                date('Ymd_His'),
95                \microtime(true)
96            )) . '.xml', $xml);
97            return response()->json();
98            // @codeCoverageIgnoreStart
99        } catch (\Exception $e) {
100            Log::critical("ARCADIA XML CREATION FAILED FOR $request->order_num : " . $e->getMessage());
101            if (!config('app.debug')) {
102                dispatch(function () use ($xml, $e, $request) {
103                    Mail::to(config('mail.MAIL_ADMIN'))->send(new CourrierArcadiaError(
104                        "ARCADIA XML CREATION FAILED FOR $request->order_num",
105                        "Une erreur est survenue lors de la création du fichier XML : " . $e->getMessage(),
106                        $xml ?: ''
107                    ));
108                })->afterResponse();
109            }
110            throw $e;
111            // @codeCoverageIgnoreEnd
112        }//end try
113    }
114
115    /**
116     * render Arcadia xml for order
117     *
118     * @param BaseApiRequest   $request
119     * @param ArcadiaMailType  $type
120     * @param ArcadiaOrderType $orderType
121     * @param array            $customerInfo
122     * @return string
123     */
124    private function makeXml(
125        BaseApiRequest $request,
126        ArcadiaMailType $type,
127        ArcadiaOrderType $orderType,
128        array $customerInfo
129    ): string {
130        $arcadiaMailType = $type->equals(ArcadiaMailType::default()) ?
131            'mail.arcadiaOrders.default' : 'mail.arcadiaOrders.game';
132        return view($arcadiaMailType, [
133            'request' => $request,
134            'type' => $type,
135            'orderType' => $orderType,
136            'customerInfo' => $customerInfo
137        ])->render();
138    }
139}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V2/CustomerController.php.html b/tests/coverage/app/Http/Controllers/Api/V2/CustomerController.php.html new file mode 100644 index 0000000..3e6d0e3 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V2/CustomerController.php.html @@ -0,0 +1,513 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V2/CustomerController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
77 / 77
CustomerController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
26
+
+ 100.00% covered (success) +
+
+
100.00%
77 / 77
 store
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
7
+
+ 100.00% covered (success) +
+
+
100.00%
25 / 25
 show
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 update
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
 destroy
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
6
+
+ 100.00% covered (success) +
+
+
100.00%
17 / 17
 authenticate
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
 activateAccount
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Enums\ApiError;
6use App\Exceptions\Api\V2\ApiErrorException;
7use App\Exceptions\Api\V2\CrmException;
8use App\Http\Controllers\Controller;
9use App\Http\Requests\Api\V2\AuthenticateRequest;
10use App\Http\Requests\Api\V2\StoreCustomerRequest;
11use App\Http\Requests\Api\V2\UpdateCustomerRequest;
12use App\Http\Resources\Api\V2\CustomerResource;
13use App\Jobs\UpdateCustomerLastConnection;
14use App\Models\Customer;
15use App\Models\Service;
16use App\Models\SingleOptin;
17use Illuminate\Database\Eloquent\ModelNotFoundException;
18use Illuminate\Http\JsonResponse;
19use Illuminate\Http\Resources\Json\JsonResource;
20use Illuminate\Support\Facades\DB;
21
22class CustomerController extends Controller
23{
24    /**
25     * Creates a new customer for the specified service.
26     *
27     * @param StoreCustomerRequest $request
28     * @param Service              $service
29     * @return JsonResource
30     * @throws ApiErrorException With ApiError::customerExists if the customer with this email already exists.
31     * @throws CrmException If saving the customer to DB failed.
32     */
33    public function store(StoreCustomerRequest $request, Service $service): JsonResource
34    {
35        $data = (object)$request->validated();
36
37        /** @var \App\Models\Customer */
38        $customer = Customer::where('email', $data->email)->firstOrCreate([
39            'email' => $data->email
40        ]);
41
42        DB::transaction(function () use (&$customer, $data, $request, $service) {
43
44            $saved = $customer->save();
45            $customer->setOptIn($service->brand, $data->opt_in);
46
47            // * Delete Single optin if exists.
48            /** @var \App\Models\SingleOptin|null $singleOptin */
49                $singleOptin = SingleOptin::where('email', $data->email)
50                ->where('brand_id', $service->brand->id)->first();
51            if ($singleOptin) {
52                $saved = $singleOptin->delete() && $saved;
53            }
54
55            /** @var \App\Models\CustomerInfo */
56            $customerInfo = $customer->info($service->brand, Customer::FIRST_OR_NEW);
57            if ($customerInfo->exists) {
58                throw new ApiErrorException(ApiError::customerExists());
59            }
60            $customerInfo->fill($request->validated());
61            $customerInfo->brand()->associate($service->brand);
62            $customerInfo->customer()->associate($customer);
63            $saved = $customerInfo->save() && $saved;
64
65            /** @var \App\Models\CustomerLogin */
66            $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_NEW);
67            $customerLogin->fill($request->validated());
68            $customerLogin->brand()->associate($service->brand);
69            $customerLogin->customer()->associate($customer);
70            $saved = $customerLogin->save() && $saved;
71
72            if (!$saved) {
73                // @codeCoverageIgnoreStart
74                throw new CrmException('Api failed to store customer.');
75                // @codeCoverageIgnoreEnd
76            }
77        });
78
79        return new CustomerResource($service->brand, $customer);
80    }
81
82    /**
83     * Renders the specific customer infos
84     *
85     * @param  Service  $service
86     * @param  Customer $customer
87     * @return JsonResource
88     */
89    public function show(Service $service, Customer $customer): JsonResource
90    {
91        // * Make sure we are accessing a customer that belongs to our service.
92        $this->authorize('customerBelongsToService', $customer);
93
94        return new CustomerResource($service->brand, $customer);
95    }
96
97    /**
98     * Updates information for a specific customer
99     *
100     * @param  UpdateCustomerRequest $request
101     * @param  Service               $service
102     * @param  Customer              $customer
103     * @return JsonResource
104     * @throws CrmException If saving the customer to DB failed.
105     */
106    public function update(UpdateCustomerRequest $request, Service $service, Customer $customer): JsonResource
107    {
108        // * Make sure we are accessing a customer that belongs to our service.
109        $this->authorize('customerBelongsToService', $customer);
110
111        DB::transaction(function () use ($request, $customer, $service) {
112            // To correct with create.
113            $customer->fill($request->validated());
114            $saved = $customer->save();
115            if ($request->has('opt_in')) {
116                $customer->setOptIn($service->brand, $request->opt_in);
117            }
118
119            /** @var \App\Models\CustomerInfo */
120            $customerInfo = $customer->info($service->brand, Customer::FIRST_OR_FAIL);
121            $customerInfo->fill($request->validated());
122            $saved = $customerInfo->save() && $saved;
123
124            /** @var \App\Models\CustomerLogin */
125            $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
126            $customerLogin->fill($request->validated());
127            $saved = $customerLogin->save() && $saved;
128
129            if (!$saved) {
130                // @codeCoverageIgnoreStart
131                throw new CrmException('Api failed to update customer.');
132                // @codeCoverageIgnoreEnd
133            }
134        });
135
136        return new CustomerResource($service->brand, $customer);
137    }
138
139    /**
140     * Deletes customer information for a specific service
141     *
142     * @param  Service  $service
143     * @param  Customer $customer
144     * @return JsonResponse
145     * @throws CrmException If removing the customer off the DB failed.
146     */
147    public function destroy(Service $service, Customer $customer): JsonResponse
148    {
149        // * Make sure we are accessing a customer that belongs to our service.
150        $this->authorize('customerBelongsToService', $customer);
151
152        // TODO: Gestion avec mesBR.
153        /** @var \App\Models\CustomerInfo */
154        $customerInfo = $customer->info($service->brand, Customer::FIRST_OR_FAIL);
155        /** @var \App\Models\CustomerLogin */
156        $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
157
158        DB::transaction(function () use ($service, $customer, $customerLogin, $customerInfo) {
159            if (
160                $customerInfo->delete() === false ||
161                $customerLogin->delete() === false
162            ) {
163                // @codeCoverageIgnoreStart
164                throw new CrmException('Api failed to delete customer.');
165                // @codeCoverageIgnoreEnd
166            };
167
168            $customer->optInHistory()->whereHas('brand', function ($q) use ($service) {
169                $q->where('id', $service->brand_id);
170            })->delete();
171
172            // * Force loading real relation counts.
173            $customer->unsetRelation('logins');
174            $customer->unsetRelation('infos');
175            $logins = $customer->logins()->count();
176            $infos  = $customer->infos()->count();
177
178            // If customer is not associated with any brand then delete.
179            if (!$logins and !$infos) {
180                if ($customer->delete() === false) {
181                    // @codeCoverageIgnoreStart
182                    throw new CrmException('Api failed to update customer.');
183                    // @codeCoverageIgnoreEnd
184                }
185            }
186        });
187
188        return response()->json();
189    }
190
191    /**
192     * Authenticate the user
193     * @param AuthenticateRequest $request
194     * @param Service             $service
195     * @return JsonResource
196     * @throws ApiErrorException With ApiError::authFailed() in case of wrong credentials.
197     * @throws ApiErrorException With ApiError::customerNotActivated() if customer account is not activated.
198     */
199    public function authenticate(AuthenticateRequest $request, Service $service): JsonResource
200    {
201        try {
202            /** @var \App\Models\Customer */
203            $customer = Customer::where('email', $request->email)->firstOrFail();
204
205            /** @var \App\Models\CustomerLogin $customerLogin */
206            $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
207            if (!$customerLogin->passwordMatch($request->password)) {
208                throw new ApiErrorException(ApiError::authFailed());
209            }
210
211            if (!$customerLogin->status) {
212                throw new ApiErrorException(ApiError::customerNotActivated());
213            }
214
215            UpdateCustomerLastConnection::dispatch($customer, $customerLogin, now())
216                ->onConnection('database')->afterResponse();
217
218            return new CustomerResource($service->brand, $customer);
219        } catch (ModelNotFoundException $e) {
220            throw new ApiErrorException(ApiError::authFailed());
221        }//end try
222    }
223
224    /**
225     * Activate the account of the user
226     * @param Customer $customer
227     * @param Service  $service
228     * @return \Illuminate\Http\JsonResponse
229     * @throws ApiErrorException With ApiError::customerAlreadyActive if customer account is already active.
230     * @throws CrmException If saving the customer to DB failed.
231     */
232    public function activateAccount(Customer $customer, Service $service)
233    {
234        // * Make sure we are accessing a customer that belongs to our service.
235        $this->authorize('customerBelongsToService', $customer);
236
237        /** @var \App\Models\CustomerLogin $login */
238        $login = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
239
240        if ($login->status) {
241            throw new ApiErrorException(ApiError::customerAlreadyActive());
242        }
243
244        $login->status = true;
245        if (!$login->save()) {
246            // @codeCoverageIgnoreStart
247            throw new CrmException('Failed to save login');
248            // @codeCoverageIgnoreEnd
249        }
250        return response()->json();
251    }
252}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V2/OptinController.php.html b/tests/coverage/app/Http/Controllers/Api/V2/OptinController.php.html new file mode 100644 index 0000000..bd933fe --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V2/OptinController.php.html @@ -0,0 +1,209 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V2/OptinController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
OptinController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
 optin
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Enums\ApiError;
6use App\Exceptions\Api\V2\ApiErrorException;
7use App\Exceptions\Api\V2\CrmException;
8use App\Http\Controllers\Controller;
9use App\Http\Requests\Api\V2\SingleOptinRequest;
10use App\Models\CustomerInfo;
11use App\Models\Service;
12use App\Models\SingleOptin;
13
14class OptinController extends Controller
15{
16    /**
17     * Creates a new Single optin or destroys it if exists
18     *
19     * @param SingleOptinRequest $request
20     * @param Service            $service
21     * @return JsonResponse
22     * @throws ApiErrorException With ApiError::singleOptinCustomerExists if this email already has a full account.
23     * @throws CrmException If the single optin could not be added or removed.
24     */
25    public function optin(SingleOptinRequest $request, Service $service)
26    {
27        if (CustomerInfo::getFromEmail($service->brand, $request->email)->exists()) {
28            throw new ApiErrorException(ApiError::singleOptinCustomerExists());
29        }
30
31        /** @var \App\Models\SingleOptin */
32        $singleOptin = SingleOptin::where('email', $request->email)
33        ->whereHas('brand', function ($q) use ($service) {
34            $q->whereId($service->brand->id);
35        })->firstOrNew();
36
37        if ($request->opt_in) {
38            $singleOptin->fill($request->validated());
39            $singleOptin->brand()->associate($service->brand);
40            $success = $singleOptin->save();
41        } else {
42            $success = $singleOptin->delete() !== false;
43        }
44
45        if (!$success) {
46            // @codeCoverageIgnoreStart
47            throw new CrmException('Api failed to update single optin.');
48            // @codeCoverageIgnoreEnd
49        }
50
51        return response()->json();
52    }
53}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V2/QuestionController.php.html b/tests/coverage/app/Http/Controllers/Api/V2/QuestionController.php.html new file mode 100644 index 0000000..8dd2849 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V2/QuestionController.php.html @@ -0,0 +1,262 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V2/QuestionController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
QuestionController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
 index
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 indexWithResponse
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 unAnsweredBy
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Http\Controllers\Controller;
6use App\Http\Resources\Api\V2\QuestionsResource;
7use App\Models\Answer;
8use App\Models\Customer;
9use App\Models\Question;
10use App\Models\Service;
11use Illuminate\Http\Resources\Json\JsonResource;
12use Illuminate\Http\Resources\Json\ResourceCollection;
13
14class QuestionController extends Controller
15{
16    /**
17     * Returns all service questions.
18     *
19     * @param  Service $service
20     * @return JsonResource
21     */
22    public function index(Service $service): ResourceCollection
23    {
24        return new QuestionsResource($service->brand->questions);
25    }
26
27    /**
28     * Returns all service questions with user answers for all of them.
29     *
30     * @param  Service  $service
31     * @param  Customer $customer
32     * @return JsonResource
33     */
34    public function indexWithResponse(Service $service, Customer $customer): ResourceCollection
35    {
36        // * Make sure we are accessing a customer that belongs to our service.
37        $this->authorize('customerBelongsToService', $customer);
38
39        $customerAnswers = $customer->getAnswers($service);
40        return new QuestionsResource($service->brand->questions, $customerAnswers);
41    }
42
43    /**
44     * Returns all service questions that was not answered by a user.
45     *
46     * @param  Service  $service
47     * @param  Customer $customer
48     * @return JsonResource
49     */
50    public function unAnsweredBy(Service $service, Customer $customer): JsonResource
51    {
52        // * Make sure we are accessing a customer that belongs to our service.
53        $this->authorize('customerBelongsToService', $customer);
54
55        $questions = Question::whereHas('brand', function ($q) use ($service) {
56            $q->where('id', $service->brand->id);
57        })->whereDoesntHave('answers', function ($q) use ($customer) {
58            $q->whereIn('id', Answer::whereHas('customers', function ($q) use ($customer) {
59                $q->where('id', $customer->id);
60            })->pluck('id'));
61        })->with('answers')->get();
62        return new QuestionsResource($questions);
63    }
64}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V2/StatsController.php.html b/tests/coverage/app/Http/Controllers/Api/V2/StatsController.php.html new file mode 100644 index 0000000..cc581b1 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V2/StatsController.php.html @@ -0,0 +1,307 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V2/StatsController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
37 / 37
StatsController
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
5
+
+ 100.00% covered (success) +
+
+
100.00%
37 / 37
 brandStats
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 questionStats
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
 answersByDays
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
27 / 27
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Http\Controllers\Controller;
6use App\Http\Requests\Api\V2\GetBrandStatsRequest;
7use App\Http\Requests\Api\V2\GetQuestionStatsRequest;
8use App\Http\Resources\Api\V2\BrandStatsResource;
9use App\Http\Resources\Api\V2\QuestionStatsResource;
10use App\Models\BrandStat;
11use App\Models\CustomerAnswer;
12use App\Models\Question;
13use App\Models\Service;
14use Illuminate\Http\Resources\Json\ResourceCollection;
15use Illuminate\Support\Carbon;
16use Illuminate\Support\Collection;
17
18class StatsController extends Controller
19{
20    /**
21     * Get specific brand statistics
22     *
23     * @param GetBrandStatsRequest $request
24     * @param Service              $service
25     * @return ResourceCollection
26     */
27    public function brandStats(GetBrandStatsRequest $request, Service $service): ResourceCollection
28    {
29        $stats = BrandStat::whereDate('created_at', '>=', $request->start)
30            ->whereDate('created_at', '<=', $request->end)
31            ->whereBrandId($service->brand->id)->get();
32        return new BrandStatsResource($stats);
33    }
34
35    /**
36     * Get statistics for a specific question
37     *
38     * @param GetQuestionStatsRequest $request
39     * @param Question                $question
40     * @return QuestionStatsResource
41     */
42    public function questionStats(GetQuestionStatsRequest $request, Question $question): QuestionStatsResource
43    {
44        // * Make sure we are accessing a customer that belongs to our service.
45        $this->authorize('questionBelongsToService', $question);
46
47        $answers = $question->answers;
48        $start   = Carbon::parse($request->start);
49        $end     = Carbon::parse($request->end);
50
51        return new QuestionStatsResource(
52            $this->answersByDays($answers, $start, $end),
53            $question
54        );
55    }
56
57    /**
58     * Total réponses par dates
59     *
60     * @param Collection $answers
61     * @param Carbon     $start
62     * @param Carbon     $end
63     * @return Collection
64     */
65    private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
66    {
67        // * Totalize by date all answers.
68        $answersByDays = CustomerAnswer::whereDate('created_at', '>=', $start)
69            ->whereDate('created_at', '<=', $end)
70            ->whereIn('answer_id', $answers->pluck('id'))
71            ->get()->map(function ($custAnswer) {
72                return [
73                    'answer_id' => $custAnswer->answer_id,
74                    'created_at' => $custAnswer->created_at->format('Y-m-d'),
75                ];
76            })->groupBy(['created_at', 'answer_id'])->map(function ($day) {
77                return $day->map(function ($custAnswers) {
78                    return $custAnswers->count();
79                });
80            });
81
82        do {
83            $currentDate = $start->format('Y-m-d');
84            /** @var \Illuminate\Support\Collection $answersByDays */
85            if (!$answersByDays->has($currentDate)) {
86                $answersByDays->put(
87                    $currentDate,
88                    $answers->mapWithKeys(function ($answer) {
89                        return [$answer->id => 0];
90                    })
91                );
92            } else {
93                $curentAnswers = $answersByDays->pull($currentDate);
94                $answersByDays->put(
95                    $currentDate,
96                    $answers->mapWithKeys(function ($answer) use ($curentAnswers) {
97                        return [$answer->id =>
98                        $curentAnswers->has($answer->id) ?
99                            $curentAnswers->get($answer->id) : 0];
100                    })
101                );
102            }
103
104            $start = $start->addDay();
105        } while ($start->lessThanOrEqualTo($end));
106
107        return $answersByDays;
108    }
109}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V2/dashboard.html b/tests/coverage/app/Http/Controllers/Api/V2/dashboard.html new file mode 100644 index 0000000..55cf407 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V2/dashboard.html @@ -0,0 +1,288 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/V2/index.html b/tests/coverage/app/Http/Controllers/Api/V2/index.html new file mode 100644 index 0000000..f892f43 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/V2/index.html @@ -0,0 +1,291 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api/V2 + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
AnswerController.php
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiController.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ArcadiaController.php
+
+ 100.00% covered (success) +
+
+
100.00%
31 / 31
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CustomerController.php
+
+ 100.00% covered (success) +
+
+
100.00%
77 / 77
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
OptinController.php
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
QuestionController.php
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
StatsController.php
+
+ 100.00% covered (success) +
+
+
100.00%
37 / 37
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/app/Http/Controllers/Api/dashboard.html b/tests/coverage/app/Http/Controllers/Api/dashboard.html new file mode 100644 index 0000000..edf9890 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/dashboard.html @@ -0,0 +1,287 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Api/index.html b/tests/coverage/app/Http/Controllers/Api/index.html new file mode 100644 index 0000000..7d05ae7 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Api/index.html @@ -0,0 +1,135 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Api + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
V1
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
V2
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ +
+ + diff --git a/tests/coverage/app/Http/Controllers/BrandController.php.html b/tests/coverage/app/Http/Controllers/BrandController.php.html new file mode 100644 index 0000000..5dec418 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/BrandController.php.html @@ -0,0 +1,158 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/BrandController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
BrandController
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 index
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Http\Controllers\Controller;
6use App\Models\Brand;
7use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
8use Kwaadpepper\CrudPolicies\Traits\CrudController;
9
10/**
11 * @codeCoverageIgnore
12 */
13class BrandController extends Controller
14{
15    use CrudController {
16        index as crudIndex;
17    }
18
19    /** @var Brand */
20    public static $modelClass = Brand::class;
21
22    /**
23     * The Bo app layout
24     *
25     * @var string
26     */
27    public static $layout = 'layouts.app';
28
29    /**
30     * Overload crud-policies index
31     *
32     * @param CrudIndexRequest $request
33     * @return \Illuminate\Http\Response
34     */
35    public function index(CrudIndexRequest $request)
36    {
37        /** @var \Illuminate\View\View */
38        $view = $this->crudIndex($request);
39        if ($request->ajax()) {
40            return $view;
41        }
42        return view('brand.index', $view->getData());
43    }
44}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/Controller.php.html b/tests/coverage/app/Http/Controllers/Controller.php.html new file mode 100644 index 0000000..6a700c9 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/Controller.php.html @@ -0,0 +1,121 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/Controller.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
Controller
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
6use Illuminate\Foundation\Bus\DispatchesJobs;
7use Illuminate\Foundation\Validation\ValidatesRequests;
8use Illuminate\Routing\Controller as BaseController;
9
10/**
11 * @codeCoverageIgnore
12 */
13class Controller extends BaseController
14{
15    use AuthorizesRequests;
16    use DispatchesJobs;
17    use ValidatesRequests;
18}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/CustomerController.php.html b/tests/coverage/app/Http/Controllers/CustomerController.php.html new file mode 100644 index 0000000..0044b3d --- /dev/null +++ b/tests/coverage/app/Http/Controllers/CustomerController.php.html @@ -0,0 +1,483 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/CustomerController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerController
n/a
0 / 0
n/a
0 / 0
32
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 index
n/a
0 / 0
13
n/a
0 / 0
 show
n/a
0 / 0
1
n/a
0 / 0
 delete
n/a
0 / 0
8
n/a
0 / 0
 export
n/a
0 / 0
1
n/a
0 / 0
 exportSubmit
n/a
0 / 0
8
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Enums\CustomerExportType;
6use App\Http\Requests\CustomerExport;
7use App\Models\Brand;
8use App\Models\Customer;
9use App\Models\CustomerInfo;
10use App\Models\CustomerLogin;
11use App\Models\SingleOptin;
12use Illuminate\Database\Query\JoinClause;
13use Illuminate\Http\Request;
14use Illuminate\Support\Carbon;
15use Illuminate\Support\Facades\DB;
16use Illuminate\Support\Facades\Schema;
17use Illuminate\Support\Facades\Session;
18use Illuminate\Support\Facades\View;
19use Illuminate\Support\Str;
20use League\Csv\Writer;
21use Symfony\Component\HttpFoundation\StreamedResponse;
22
23/**
24 * @codeCoverageIgnore
25 */
26class CustomerController extends Controller
27{
28    /**
29     * View share crud policies modelClass
30     */
31    public function __construct()
32    {
33        $this->middleware(function ($request, $next) {
34            View::share('modelClass', Customer::class);
35            return $next($request);
36        });
37    }
38
39    /**
40     * View all customers
41     *
42     * @param Request $request
43     * @return \Illuminate\Http\Response
44     */
45    public function index(Request $request)
46    {
47        $modelTable = (new Customer())->getTable();
48        $modelClass = Customer::class;
49        $brands     = Brand::select('id', 'name')->get();
50        $columns    = ['customer_infos.lastname', 'customer_infos.firstname'];
51
52        // * Jointure pour pouvoir trier les colonnes.
53        /** @var \Illuminate\Database\Eloquent\Builder */
54        $query = CustomerInfo::join('customer_logins', function (JoinClause $join) {
55            $join->on('customer_logins.brand_id', '=', 'customer_infos.brand_id')
56            ->on('customer_logins.customer_id', '=', 'customer_infos.customer_id');
57        })
58        ->join('customers', function (JoinClause $join) {
59            $join->on('customers.id', '=', 'customer_infos.customer_id');
60        })
61        ->join('brands', function (JoinClause $join) {
62            $join->on('brands.id', '=', 'customer_infos.brand_id');
63        });
64        if (request()->brand or Session::has("crud.$modelTable.brand")) {
65            $brand = $request->brand ?? Session::get("crud.$modelTable.brand");
66            Session::put("crud.$modelTable.brand", $brand);
67            $query = $query->where('customer_infos.brand_id', $brand);
68        }
69
70        // * Search on crud Label column.
71        if ($request->search or Session::has("crud.$modelTable.search")) {
72            $search = $request->search ?? Session::get("crud.$modelTable.search");
73            Session::put("crud.$modelTable.search", $search);
74            $query->where(function ($q) use ($columns, $search) {
75                foreach ($columns as $column) {
76                    $q = $q->orWhere($column, 'LIKE', "%$search%");
77                }
78                $q = $q->orWhere('customers.email', 'LIKE', "%$search%");
79                $q = $q->orWhere('customer_logins.last_connection', 'LIKE', "%$search%");
80            });
81        }
82
83        // * Sort columns.
84        if ($request->sort_col) {
85            Session::put("crud.$modelTable.sort_col", $request->sort_col);
86        }
87        if ($request->sort_way) {
88            Session::put("crud.$modelTable.sort_way", $request->sort_way);
89        }
90
91        if ($request->rst) {
92            Session::remove("crud.$modelTable.brand");
93            Session::remove("crud.$modelTable.search");
94            Session::remove("crud.$modelTable.sort_way");
95            Session::remove("crud.$modelTable.sort_col");
96        }
97        if (
98            Session::has("crud.$modelTable.sort_col") and
99            Session::has("crud.$modelTable.sort_way") and
100            (Schema::hasColumn($modelTable, Session::get("crud.$modelTable.sort_col")) or
101            in_array(Session::get("crud.$modelTable.sort_col"), [
102                'brands.name',
103                'customers.email',
104                'customer_infos.firstname',
105                'customer_infos.lastname',
106                'customer_infos.opt_in',
107                'customer_logins.last_connection',
108                'customer_logins.status'
109            ]))
110        ) {
111            $query = $query->orderBy(
112                Session::get("crud.$modelTable.sort_col"),
113                Session::get("crud.$modelTable.sort_way")
114            );
115        }
116
117        /** @var \Illuminate\Pagination\AbstractPaginator */
118        $customers = $query->paginate(config('crud.paginate', 50));
119
120        return view('customer.index', compact(
121            'modelClass',
122            'modelTable',
123            'customers',
124            'brands'
125        ));
126    }
127
128    /**
129     * Show a specific custmer
130     *
131     * @param Customer $customer
132     * @param Brand    $brand
133     * @return \Illuminate\Http\Response
134     */
135    public function show(Customer $customer, Brand $brand)
136    {
137        $customerInfos = CustomerInfo::where('customer_id', $customer->id)
138            ->where('brand_id', $brand->id)->firstOrFail();
139        $customerLogin = CustomerLogin::where('customer_id', $customer->id)
140            ->where('brand_id', $brand->id)->first();
141
142        return view('customer.show', compact(
143            'customerInfos',
144            'customerLogin'
145        ));
146    }
147
148    /**
149     * Delete a specific customer info
150     *
151     * @param Customer $customer
152     * @param Brand    $brand
153     * @return \Illuminate\Http\RedirectResponse
154     */
155    public function delete(Customer $customer, Brand $brand)
156    {
157        try {
158            // TODO: Gestion avec mesBR.
159            /** @var \App\Models\CustomerInfo */
160            $customerInfo = $customer->info($brand, Customer::FIRST_OR_FAIL);
161            /** @var \App\Models\CustomerLogin */
162            $customerLogin = $customer->login($brand, Customer::FIRST_OR_FAIL);
163
164            DB::transaction(function () use (
165                $brand,
166                $customer,
167                $customerLogin,
168                $customerInfo
169            ) {
170                if (
171                    $customerInfo->delete() === false ||
172                    $customerLogin->delete() === false
173                ) {
174                    throw new \ErrorException('Api failed to delete customer.');
175                };
176
177                $customer->optInHistory()->whereHas('brand', function ($q) use ($brand) {
178                    $q->where('id', $brand->id);
179                })->delete();
180
181                $logins  = $customer->logins()->count();
182                $infos   = $customer->infos()->count();
183                $history = $customer->optInHistory()->count();
184
185                // If customer is not associated with any brand then delete.
186                if (!$logins and !$infos and !$history) {
187                    if ($customer->delete() === false) {
188                        throw new \ErrorException('Api failed to update customer.');
189                    }
190                }
191            });
192        } catch (\ErrorException $e) {
193            return redirect()->route('customers.index')
194            ->with('error', trans('Le compte n\'as pas pu être supprimé.'));
195        } //end try
196
197        return redirect()->route('customers.index')->with('warning', trans('Compte supprimé.'));
198    }
199
200    /**
201     * Show export the customers form
202     *
203     * @return \Illuminate\Http\Response
204     */
205    public function export()
206    {
207        $brands = Brand::all();
208        return view('customer.export', compact(
209            'brands'
210        ));
211    }
212
213    /**
214     * Exports customers following the requested export type
215     *
216     * @param CustomerExport $request
217     * @return \Symfony\Component\HttpFoundation\StreamedResponse
218     * @throws \Exception If the request export type in not handled.
219     */
220    public function exportSubmit(CustomerExport $request)
221    {
222        $pagination = 2000;
223        $header     = '"lastname";"firstname";"email";"hashcode"';
224
225        $w = Writer::createFromString($header . PHP_EOL);
226        $w->setDelimiter(';');
227        $w->setEnclosure('"');
228        $w->setOutputBOM(Writer::BOM_UTF8);
229
230        $exportType = CustomerExportType::make(\intval($request->export_type));
231
232        // * We send the CSV using Symfony StreamedResponse.
233        $response = new StreamedResponse();
234        $response->headers->set('Content-Encoding', 'none');
235        $response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
236        $response->headers->set('Description', 'File Transfer');
237        $response->headers->set(
238            'Content-Disposition',
239            sprintf(
240                'attachment; filename="Export pour newsletter %s %s.csv"',
241                Carbon::now()->format('d-m-Y_H-i'),
242                Str::slug($exportType)
243            )
244        );
245
246        switch ($exportType) {
247            case CustomerExportType::both():
248                $continue = true;
249                // ! Continue
250            case CustomerExportType::customer():
251                $page = 1;
252                /** @var \Illuminate\Database\Eloquent\Builder $req */
253                $req   = CustomerInfo::where('brand_id', $request->brand)
254                ->where('opt_in', true);
255                $total = $req->count();
256                $pages = \ceil($total / $pagination);
257                do {
258                    /** @var \Illuminate\Pagination\LengthAwarePaginator $items */
259                    $items = $req->with('customer', function ($q) {
260                        $q->select(['id', 'email']);
261                    })->paginate(
262                        $pagination,
263                        ['lastname', 'firstname', 'hashcode', 'customer_id'],
264                        'page',
265                        $page
266                    );
267                    $items->map(function (CustomerInfo $customerInfo) use ($w) {
268                        $w->insertOne([
269                            'lastname' => $customerInfo->lastname,
270                            'firstname' => $customerInfo->firstname,
271                            'email' => $customerInfo->customer->email,
272                            'hashcode' => $customerInfo->hashcode
273                        ]);
274                    });
275                } while ($page++ <= $pages);
276                if (!isset($continue)) {
277                    break;
278                }
279                // ! Continue
280            case CustomerExportType::singleOptin():
281                SingleOptin::where('brand_id', $request->brand)->get()
282                    ->map(function (SingleOptin $singleOptin) use ($w) {
283                        $w->insertOne([
284                            'lastname' => '',
285                            'firstname' => '',
286                            'email' => $singleOptin->email,
287                            'hashcode' => ''
288                        ]);
289                    });
290                break;
291            default:
292                throw new \Exception(sprintf('Unhandled CustomerExportType %s', $exportType->label));
293        } //end switch
294
295        /*
296            We create a callable to output the CSV in chunk
297            with Symfony StreamResponse you can flush the body content if necessary
298            see Symfony documentation for more information
299        */
300
301        // * The flush value should depend on your CSV size.
302        $flush_threshold = 1000;
303
304        $response->setCallback(function () use ($w, $flush_threshold) {
305            foreach ($w->chunk(1024) as $offset => $chunk) {
306                echo $chunk;
307                if ($offset % $flush_threshold === 0) {
308                    flush();
309                }
310            }
311        });
312        return $response;
313    }
314}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/LoginController.php.html b/tests/coverage/app/Http/Controllers/LoginController.php.html new file mode 100644 index 0000000..85c526d --- /dev/null +++ b/tests/coverage/app/Http/Controllers/LoginController.php.html @@ -0,0 +1,190 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/LoginController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
LoginController
n/a
0 / 0
n/a
0 / 0
5
n/a
0 / 0
 index
n/a
0 / 0
1
n/a
0 / 0
 authenticate
n/a
0 / 0
3
n/a
0 / 0
 logout
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use Illuminate\Http\Request;
6use Illuminate\Support\Facades\Auth;
7
8/**
9 * @codeCoverageIgnore
10 */
11class LoginController extends Controller
12{
13    /**
14     * Shows the login view
15     *
16     * @return \Illuminate\Http\Response
17     */
18    public function index()
19    {
20        return view('login');
21    }
22
23    /**
24     * Undocumented function
25     *
26     * @param Request $request
27     * @return \Illuminate\Http\Response
28     */
29    public function authenticate(Request $request)
30    {
31        if (
32            Auth::attempt(
33                ["email" => $request->email, "password" => $request->password],
34                $request->remember !== null ? true : false
35            )
36        ) {
37            return redirect()->route('customers.index');
38        } else {
39            return back()->withInput($request->except("password"))
40                ->withErrors("Les identifiants entrés sont incorrects, veuillez réessayer.");
41        }
42    }
43
44    /**
45     * Undocumented function
46     *
47     * @return \Illuminate\Http\Response
48     */
49    public function logout()
50    {
51        Auth::logout();
52        return redirect()->route('login');
53    }
54}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/QuestionController.php.html b/tests/coverage/app/Http/Controllers/QuestionController.php.html new file mode 100644 index 0000000..1ec578f --- /dev/null +++ b/tests/coverage/app/Http/Controllers/QuestionController.php.html @@ -0,0 +1,306 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/QuestionController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
QuestionController
n/a
0 / 0
n/a
0 / 0
12
n/a
0 / 0
 indexQuery
n/a
0 / 0
2
n/a
0 / 0
 index
n/a
0 / 0
2
n/a
0 / 0
 export
n/a
0 / 0
1
n/a
0 / 0
 exportSubmit
n/a
0 / 0
7
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Http\Controllers\Controller;
6use App\Http\Requests\QuestionExport;
7use App\Models\Brand;
8use App\Models\Question;
9use Carbon\Carbon;
10use Illuminate\Database\Eloquent\Builder;
11use Illuminate\Support\Facades\DB;
12use Illuminate\Support\Str;
13use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
14use Kwaadpepper\CrudPolicies\Traits\CrudController;
15use League\Csv\Writer;
16use Symfony\Component\HttpFoundation\StreamedResponse;
17
18/**
19 * @codeCoverageIgnore
20 */
21class QuestionController extends Controller
22{
23    use CrudController {
24        index as crudIndex;
25    }
26
27    /** @var Question */
28    public static $modelClass = Question::class;
29
30    /**
31     * The Bo app layout
32     *
33     * @var string
34     */
35    public static $layout = 'layouts.app';
36
37    /**
38     * Event index crud-policies
39     *
40     * @param Builder $query
41     * @return Builder
42     */
43    public function indexQuery(Builder $query): Builder
44    {
45        if (request()->brand) {
46            $query = $query->where('brand_id', '=', request()->brand);
47        }
48        return $query;
49    }
50
51    /**
52     * Overload crud-policies index
53     *
54     * @param CrudIndexRequest $request
55     * @return \Illuminate\Http\Response
56     */
57    public function index(CrudIndexRequest $request)
58    {
59        /** @var \Illuminate\View\View */
60        $view = $this->crudIndex($request);
61        if ($request->ajax()) {
62            return $view;
63        }
64        return view('question.index', $view->getData());
65    }
66
67    /**
68     * Shows the question export form
69     *
70     * @return \Illuminate\Http\Response
71     */
72    public function export()
73    {
74        $brands = Brand::all();
75        return view('question.export', compact('brands'));
76    }
77
78    /**
79     * Exports questions as csv the requested export type
80     *
81     * @param QuestionExport $request
82     * @return \Symfony\Component\HttpFoundation\StreamedResponse
83     */
84    public function exportSubmit(QuestionExport $request)
85    {
86        $results   = [];
87        $brand     = Brand::findOrFail($request->brand);
88        $questions = $brand->questions->load('answers');
89
90        foreach ($questions as $question) {
91            $answersCounts = DB::query()
92                ->selectRaw('count(*) as total, answer_id')
93                ->from('customer_answer')
94                ->whereIn('answer_id', $question->answers->pluck('id'))
95                ->groupBy('answer_id')
96                ->get()->keyBy('answer_id');
97
98            $results[$question->id] = [
99                'question' => $question->question,
100                'answers' => []
101            ];
102            foreach ($question->answers as $answer) {
103                $results[$question->id]['answers'][] =
104                    [
105                        "$answer->answer ",
106                        $answersCounts[$answer->id]->total ?? 0
107                    ];
108            }
109        }
110
111        $w = Writer::createFromString('');
112        $w->setDelimiter(';');
113        $w->setEnclosure('"');
114        $w->setOutputBOM(Writer::BOM_UTF8);
115
116        // * We send the CSV using Symfony StreamedResponse.
117        $response = new StreamedResponse();
118        $response->headers->set('Content-Encoding', 'none');
119        $response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
120        $response->headers->set('Description', 'File Transfer');
121        $response->headers->set(
122            'Content-Disposition',
123            sprintf(
124                'attachment; filename="Export pour newsletter %s %s.csv"',
125                Carbon::now()->format('d-m-Y_H-i'),
126                Str::slug($brand->name)
127            )
128        );
129
130        foreach ($results as $result) {
131            $w->insertOne([
132                'question' => $result['question'],
133            ]);
134            foreach ($result['answers'] as $answer) {
135                $w->insertOne($answer);
136            }
137            $w->insertOne([]);
138        }
139
140        /*
141            We create a callable to output the CSV in chunk
142            with Symfony StreamResponse you can flush the body content if necessary
143            see Symfony documentation for more information
144        */
145
146        // * The flush value should depend on your CSV size.
147        $flush_threshold = 1000;
148
149        $response->setCallback(function () use ($w, $flush_threshold) {
150            foreach ($w->chunk(1024) as $offset => $chunk) {
151                echo $chunk;
152                if ($offset % $flush_threshold === 0) {
153                    flush();
154                }
155            }
156        });
157        return $response;
158    }
159}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/ServiceController.php.html b/tests/coverage/app/Http/Controllers/ServiceController.php.html new file mode 100644 index 0000000..5097e02 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/ServiceController.php.html @@ -0,0 +1,197 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/ServiceController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ServiceController
n/a
0 / 0
n/a
0 / 0
4
n/a
0 / 0
 index
n/a
0 / 0
2
n/a
0 / 0
 generateServicePassword
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Http\Requests\GenerateServiceToken;
6use App\Mail\ServiceCredentialRenewed;
7use App\Models\Service;
8use Illuminate\Support\Facades\Hash;
9use Illuminate\Support\Facades\Mail;
10use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
11use Kwaadpepper\CrudPolicies\Traits\CrudController;
12
13/**
14 * @codeCoverageIgnore
15 */
16class ServiceController extends Controller
17{
18    use CrudController {
19        index as crudIndex;
20    }
21
22    /** @var Service */
23    public static $modelClass = Service::class;
24
25    /**
26     * The Bo app layout
27     *
28     * @var string
29     */
30    public static $layout = 'layouts.app';
31
32    /**
33     * Overload crud-policies index
34     *
35     * @param CrudIndexRequest $request
36     * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
37     */
38    public function index(CrudIndexRequest $request)
39    {
40        /** @var \Illuminate\View\View */
41        $view = $this->crudIndex($request);
42        if ($request->ajax()) {
43            return $view;
44        }
45        return view('service.index', $view->getData());
46    }
47
48    /**
49     * Regen service credentials
50     *
51     * @param GenerateServiceToken $request
52     * @param Service              $service
53     * @return \Illuminate\Http\RedirectResponse
54     */
55    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed, Squiz.Commenting.FunctionComment.Missing
56    public function generateServicePassword(GenerateServiceToken $request, Service $service)
57    {
58        /** @var \App\Models\User $user */
59        $user = auth()->user();
60
61        // * Special Condition for migration from v2 to v3.
62        $service->bearer_token = !Hash::needsRehash($service->bearer_token) ?
63            $service->generateBearerToken() : $service->bearer_token;
64
65        $bearerPwd = $service->generateBearerPassword();
66        $service->saveOrFail();
67        Mail::send(new ServiceCredentialRenewed($service, $user));
68        return back()
69            // phpcs:ignore Generic.Files.LineLength.TooLong
70            ->with('error', "ATTENTION : METTEZ À JOUR LE SITE ! \nNouveau token d'API de $service->name : '$service->bearer_token' et mot de passe '$bearerPwd'");
71    }
72}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/SingleOptinController.php.html b/tests/coverage/app/Http/Controllers/SingleOptinController.php.html new file mode 100644 index 0000000..b898d1e --- /dev/null +++ b/tests/coverage/app/Http/Controllers/SingleOptinController.php.html @@ -0,0 +1,183 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/SingleOptinController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
SingleOptinController
n/a
0 / 0
n/a
0 / 0
4
n/a
0 / 0
 indexQuery
n/a
0 / 0
2
n/a
0 / 0
 index
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Models\SingleOptin;
6use Illuminate\Database\Eloquent\Builder;
7use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
8use Kwaadpepper\CrudPolicies\Traits\CrudController;
9
10/**
11 * @codeCoverageIgnore
12 */
13class SingleOptinController extends Controller
14{
15    use CrudController {
16        index as crudIndex;
17    }
18
19    /** @var SingleOptin */
20    public static $modelClass = SingleOptin::class;
21
22    /**
23     * The Bo app layout
24     *
25     * @var string
26     */
27    public static $layout = 'layouts.app';
28
29    /**
30     * Event index crud-policies
31     *
32     * @param Builder $query
33     * @return Builder
34     */
35    public function indexQuery(Builder $query): Builder
36    {
37        if (request()->brand) {
38            $query = $query->where('brand_id', request()->brand);
39        }
40        return $query;
41    }
42
43    /**
44     * Overload crud-policies index
45     *
46     * @param CrudIndexRequest $request
47     * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
48     */
49    public function index(CrudIndexRequest $request)
50    {
51        /** @var \Illuminate\View\View */
52        $view = $this->crudIndex($request);
53        if ($request->ajax()) {
54            return $view;
55        }
56        return view('single_optin.index', $view->getData());
57    }
58}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/StatsController.php.html b/tests/coverage/app/Http/Controllers/StatsController.php.html new file mode 100644 index 0000000..9a8a021 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/StatsController.php.html @@ -0,0 +1,356 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers/StatsController.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StatsController
n/a
0 / 0
n/a
0 / 0
12
n/a
0 / 0
 viewStatsByBrand
n/a
0 / 0
2
n/a
0 / 0
 exportByBrandCsv
n/a
0 / 0
3
n/a
0 / 0
 viewByQuestion
n/a
0 / 0
2
n/a
0 / 0
 exportByQuestionCsv
n/a
0 / 0
2
n/a
0 / 0
 answersByDays
n/a
0 / 0
3
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Controllers;
4
5use App\Http\Requests\StatsByBrandRequest;
6use App\Http\Requests\StatsByQuestionRequest;
7use App\Models\Brand;
8use App\Models\BrandStat;
9use App\Models\CustomerAnswer;
10use App\Models\Question;
11use Illuminate\Support\Carbon;
12use Illuminate\Support\Collection;
13use Illuminate\Support\Facades\Schema;
14use League\Csv\Writer;
15
16/**
17 * @codeCoverageIgnore
18 */
19class StatsController extends Controller
20{
21    /**
22     * Shows stats by brand
23     *
24     * @param StatsByBrandRequest $request
25     * @return \Illuminate\Http\Response
26     */
27    public function viewStatsByBrand(StatsByBrandRequest $request)
28    {
29        $stats = null;
30        if ($request->post()) {
31            $stats = BrandStat::whereDate('created_at', '>=', $request->start)
32                ->whereDate('created_at', '<=', $request->end)
33                ->whereBrandId($request->brand)->get();
34        }
35        return view('stats.view-stats-by-brands', \compact('stats'));
36    }
37
38    /**
39     * Exports stats by brand as a csv file
40     *
41     * @param StatsByBrandRequest $request
42     * @return \Illuminate\Http\Response
43     */
44    public function exportByBrandCsv(StatsByBrandRequest $request)
45    {
46        $brand = Brand::findOrFail($request->brand);
47        $stats = BrandStat::with('brand')->whereDate('created_at', '>=', $request->start)
48            ->whereDate('created_at', '<=', $request->end)
49            ->whereBrandId($request->brand)->get();
50
51        $columns = collect(Schema::getColumnListing((new BrandStat())->getTable()))
52            ->filter(function ($value) {
53                return !in_array($value, ['created_at', 'brand_id']);
54            })->all();
55
56        $header = '"date";"marque";' . implode(';', $columns);
57
58        $w = Writer::createFromString($header . PHP_EOL);
59        $w->setDelimiter(';');
60        $w->setEnclosure('"');
61        $w->setOutputBOM(Writer::BOM_UTF8);
62
63        foreach ($stats as $stat) {
64            $cols = [
65                $stat->created_at->format('Y-m-d'),
66                $stat->brand->name
67            ];
68            foreach ($columns as $colname) {
69                $cols[] = $stat->{$colname};
70            }
71            $w->insertOne($cols);
72        }
73        return response((string)$w, 200, [
74            'Content-Encoding' => 'none',
75            'Content-Type' => 'text/csv; charset=UTF-8',
76            // phpcs:ignore Generic.Files.LineLength.TooLong
77            'Content-Disposition' => "attachment; filename=\"Statistiques du $request->start au $request->end pour $brand->name.csv\"",
78            'Content-Description' => 'File Transfer',
79        ]);
80    }
81
82    /**
83     * Views stats for a specific question
84     *
85     * @param StatsByQuestionRequest $request
86     * @return \Illuminate\Http\Response
87     */
88    public function viewByQuestion(StatsByQuestionRequest $request)
89    {
90        $question      = null;
91        $answers       = null;
92        $answersByDays = null;
93        if ($request->post()) {
94            $question = Question::findOrFail($request->question);
95            $answers  = $question->answers;
96            $start    = Carbon::parse($request->start);
97            $end      = Carbon::parse($request->end);
98
99            $answersByDays = $this->answersByDays($answers, $start, $end);
100        }
101
102        return view('stats.view-stats-by-questions', \compact('question', 'answers', 'answersByDays'));
103    }
104
105    /**
106     * Exports stats for a specific question as csv
107     *
108     * @param StatsByQuestionRequest $request
109     * @return \Illuminate\Http\Response
110     */
111    public function exportByQuestionCsv(StatsByQuestionRequest $request)
112    {
113        $question = Question::findOrFail($request->question);
114        $answers  = $question->answers;
115        $start    = Carbon::parse($request->start);
116        $end      = Carbon::parse($request->end);
117
118        $answersByDays = $this->answersByDays($answers, $start, $end);
119
120        $header = '"date";' . implode(';', $answers->map(function ($answer) {
121            return "\"$answer->answer\"";
122        })->all());
123
124        $w = Writer::createFromString($header . PHP_EOL);
125        $w->setDelimiter(';');
126        $w->setEnclosure('"');
127        $w->setOutputBOM(Writer::BOM_UTF8);
128
129        foreach ($answersByDays as $day => $dayAnswers) {
130            $w->insertOne(array_merge(
131                [
132                    $day
133                ],
134                $dayAnswers->values()->all()
135            ));
136        }
137        return response((string)$w, 200, [
138            'Content-Encoding' => 'none',
139            'Content-Type' => 'text/csv; charset=UTF-8',
140            // phpcs:ignore Generic.Files.LineLength.TooLong
141            'Content-Disposition' => "attachment; filename=\"Statistiques du $request->start au $request->end pour $question->question.csv\"",
142            'Content-Description' => 'File Transfer',
143        ]);
144    }
145
146    /**
147     * Makes a collection of total users answers by day
148     *
149     * @param Collection $answers A collection of Answer.
150     * @param Carbon     $start   The wanted start period.
151     * @param Carbon     $end     The wanted end period.
152     * @return Collection A collection with date as Y_m_d index with array of answers and their total.
153     */
154    private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
155    {
156        // * Totalize by date all answers.
157        $answersByDays = CustomerAnswer::whereDate('created_at', '>=', $start)
158            ->whereDate('created_at', '<=', $end)
159            ->whereIn('answer_id', $answers->pluck('id'))
160            ->get()->map(function ($custAnswer) {
161                return [
162                    'answer_id' => $custAnswer->answer_id,
163                    'created_at' => $custAnswer->created_at->format('Y-m-d'),
164                ];
165            })->groupBy(['created_at', 'answer_id'])->map(function ($day) {
166                return $day->map(function ($custAnswers) {
167                    return $custAnswers->count();
168                });
169            });
170
171        do {
172            $currentDate = $start->format('Y-m-d');
173            /** @var \Illuminate\Support\Collection $answersByDays */
174            if (!$answersByDays->has($currentDate)) {
175                $answersByDays->put(
176                    $currentDate,
177                    $answers->mapWithKeys(function ($answer) {
178                        return [$answer->answer => 0];
179                    })
180                );
181            } else {
182                $curentAnswers = $answersByDays->pull($currentDate);
183                $answersByDays->put(
184                    $currentDate,
185                    $answers->mapWithKeys(function ($answer) use ($curentAnswers) {
186                        return [$answer->answer =>
187                        $curentAnswers->has($answer->id) ?
188                        $curentAnswers->get($answer->id) : 0];
189                    })
190                );
191            }
192
193            $start = $start->addDay();
194        } while ($start->lessThanOrEqualTo($end));
195
196        return $answersByDays;
197    }
198}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/dashboard.html b/tests/coverage/app/Http/Controllers/dashboard.html new file mode 100644 index 0000000..85db654 --- /dev/null +++ b/tests/coverage/app/Http/Controllers/dashboard.html @@ -0,0 +1,286 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http/Controllers + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/Controllers/index.html b/tests/coverage/app/Http/Controllers/index.html new file mode 100644 index 0000000..c4efffd --- /dev/null +++ b/tests/coverage/app/Http/Controllers/index.html @@ -0,0 +1,238 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Controllers + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
Api
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
AnswerController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
BrandController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Controller.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
CustomerController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
LoginController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
QuestionController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ServiceController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
SingleOptinController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StatsController.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Http/Kernel.php.html b/tests/coverage/app/Http/Kernel.php.html new file mode 100644 index 0000000..edfaa65 --- /dev/null +++ b/tests/coverage/app/Http/Kernel.php.html @@ -0,0 +1,171 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Kernel.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
Kernel
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http;
4
5use Illuminate\Foundation\Http\Kernel as HttpKernel;
6
7class Kernel extends HttpKernel
8{
9    /**
10     * The application's global HTTP middleware stack.
11     *
12     * These middleware are run during every request to your application.
13     *
14     * @var array
15     */
16    protected $middleware = [
17        \App\Http\Middleware\TrustProxies::class,
18        \Fruitcake\Cors\HandleCors::class,
19        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
20        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
21        \App\Http\Middleware\TrimStrings::class,
22        \App\Http\Middleware\XssSanitizer::class,
23        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class
24    ];
25
26    /**
27     * The application's route middleware groups.
28     *
29     * @var array
30     */
31    protected $middlewareGroups = [
32        'web' => [
33            \App\Http\Middleware\EncryptCookies::class,
34            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
35            \Illuminate\Session\Middleware\StartSession::class,
36            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
37            \App\Http\Middleware\VerifyCsrfToken::class,
38            \Illuminate\Routing\Middleware\SubstituteBindings::class,
39        ],
40
41        'api' => [
42            'throttle:api',
43            \App\Http\Middleware\JsonOnly::class,
44            \App\Http\Middleware\ApiResponse::class,
45            \App\Http\Middleware\ApiCheckTimeStamps::class,
46            \Illuminate\Routing\Middleware\SubstituteBindings::class
47        ],
48    ];
49
50    /**
51     * The application's route middleware.
52     *
53     * These middleware may be assigned to groups or used individually.
54     *
55     * @var array
56     */
57    protected $routeMiddleware = [
58        'auth' => \App\Http\Middleware\Authenticate::class,
59        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
60        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
61        'can' => \Illuminate\Auth\Middleware\Authorize::class,
62        'check-brand' => \App\Http\Middleware\CheckBrand::class,
63        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
64        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
65        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
66        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
67        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
68    ];
69}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/ApiCheckTimeStamps.php.html b/tests/coverage/app/Http/Middleware/ApiCheckTimeStamps.php.html new file mode 100644 index 0000000..4c0b56b --- /dev/null +++ b/tests/coverage/app/Http/Middleware/ApiCheckTimeStamps.php.html @@ -0,0 +1,195 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/ApiCheckTimeStamps.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
ApiCheckTimeStamps
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 handle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Enums\ApiError;
6use App\Exceptions\Api\V2\ApiErrorException;
7use App\Lib\Helper;
8use Illuminate\Http\Request;
9use Illuminate\Support\Carbon;
10
11/**
12 * Check Http Header Timestamp with request timestamp
13 * to prevent replay attacks
14 * https://restfulapi.net/security-essentials/
15 */
16class ApiCheckTimeStamps
17{
18    /**
19     * Handle an incoming request.
20     *
21     * @param  \Illuminate\Http\Request $request
22     * @param  \Closure                 $next
23     * @return mixed
24     * @throws ApiErrorException With ApiError::wrongTimeStamp if timestamp has an incorrect format or is timed out.
25     */
26    public function handle(Request $request, \Closure $next)
27    {
28        if (Helper::isRequestForApiV2($request)) {
29            if (!\is_string($request->timestamp) or !Carbon::hasFormat($request->timestamp, 'Y-m-d H:i:s')) {
30                throw new ApiErrorException(ApiError::wrongTimeStamp());
31            }
32            if (
33                Carbon::parse($request->timestamp, 'utc')
34                ->isBefore(Carbon::now('UTC')->subSeconds(config('api.timestampTimeout', 10)))
35            ) {
36                throw new ApiErrorException(ApiError::wrongTimeStamp());
37            }
38        }
39        return $next($request);
40    }
41}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/ApiResponse.php.html b/tests/coverage/app/Http/Middleware/ApiResponse.php.html new file mode 100644 index 0000000..69a2f81 --- /dev/null +++ b/tests/coverage/app/Http/Middleware/ApiResponse.php.html @@ -0,0 +1,227 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/ApiResponse.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
ApiResponse
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
10
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
 handle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
10
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Enums\ApiStatus;
6use App\Http\Resources\Api\V2\ApiResponse as V2ApiResponse;
7use App\Lib\Helper;
8use App\Models\Service;
9use Illuminate\Database\Eloquent\ModelNotFoundException;
10use Illuminate\Http\JsonResponse;
11use Illuminate\Http\Request;
12use Illuminate\Support\Facades\Log;
13
14class ApiResponse
15{
16    /**
17     * Handle an incoming request.
18     *
19     * @param  \Illuminate\Http\Request $request
20     * @param  \Closure                 $next
21     * @return mixed
22     * @throws \ErrorException If the received response is not a JsonResponse.
23     */
24    public function handle(Request $request, \Closure $next)
25    {
26        /** @var \Illuminate\Http\JsonResponse $response */
27        $response = $next($request);
28
29        if (\get_class($response) !== JsonResponse::class) {
30            // @codeCoverageIgnoreStart
31            throw new \ErrorException('All api responses should be json.');
32            // @codeCoverageIgnoreEnd
33        }
34
35        // Set no cache header.
36        $response->header('Cache-Control', 'No-Store');
37
38        if (Helper::isRequestForApiV1($request)) {
39            // @codeCoverageIgnoreStart
40            try {
41                $bearerToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token ?? '';
42                $service     = Service::getFromBearerToken($bearerToken);
43                $service->incrementStat();
44            } catch (ModelNotFoundException $e) {
45                Log::error(sprintf('Could not increment stats %s', $e->getMessage()));
46            }
47            return $response;
48            // @codeCoverageIgnoreEnd
49        }
50
51        /** @var \App\Models\Service|null */
52        if (\optional($service = app(Service::class))->id) {
53            $service->incrementStat();
54        }
55
56        /** @var object $data */
57        $data = $response->getData();
58
59        // If reponse is already a V2ApiResponse then just return it. This happens if error handler comes to action.
60        if (
61            isset($data->status) and
62            isset($data->data) and
63            isset($data->version) and
64            in_array($data->status, [ApiStatus::OK()->value, ApiStatus::KO()->value])
65        ) {
66            return $response;
67        }
68
69        /** @var \App\Enums\ApiStatus $status */
70        $status = intval($response->getStatusCode() / 100) == 2 ? ApiStatus::OK() : ApiStatus::KO();
71        return $response->setData(new V2ApiResponse((object)$response->getData(), $status));
72    }
73}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/Authenticate.php.html b/tests/coverage/app/Http/Middleware/Authenticate.php.html new file mode 100644 index 0000000..493dc25 --- /dev/null +++ b/tests/coverage/app/Http/Middleware/Authenticate.php.html @@ -0,0 +1,223 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/Authenticate.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
Authenticate
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 handle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 redirectTo
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Lib\Helper;
6use App\Models\Service;
7use Closure;
8use Illuminate\Auth\Middleware\Authenticate as Middleware;
9
10/**
11 * Updated for ApiV2
12 */
13class Authenticate extends Middleware
14{
15    /**
16     * Handle an incoming request.
17     *
18     * @param  mixed    $request
19     * @param  \Closure $next
20     * @param  mixed    ...$guards
21     * @return mixed
22     *
23     * @throws \Illuminate\Auth\AuthenticationException If authentification fails.
24     */
25    public function handle($request, Closure $next, ...$guards)
26    {
27        if (Helper::isRequestForApiV1($request)) {
28            // @codeCoverageIgnoreStart
29            // Use patched v1 Auth mecanism (without bearer reimplementation).
30            $bearerToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token ?? '';
31            $service     = Service::getFromBearerToken($bearerToken);
32            $request->request->add(['brand_id' => $service->brand->id]);
33            $response = $next($request);
34            // @codeCoverageIgnoreEnd
35        } else {
36            // Use Laravel auth check mecanism.
37            $response = \call_user_func_array(
38                [$this, 'parent::' . __FUNCTION__],
39                array_merge([$request, $next], $guards)
40            );
41        }
42
43        return $response;
44    }
45    /**
46     * Get the path the user should be redirected to when they are not authenticated.
47     *
48     * @param  mixed $request
49     * @return string|null
50     * @codeCoverageIgnore
51     */
52    protected function redirectTo($request)
53    {
54        if (! $request->expectsJson()) {
55            return route('login');
56        }
57    }
58}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/CheckBrand.php.html b/tests/coverage/app/Http/Middleware/CheckBrand.php.html new file mode 100644 index 0000000..6243ecb --- /dev/null +++ b/tests/coverage/app/Http/Middleware/CheckBrand.php.html @@ -0,0 +1,154 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/CheckBrand.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CheckBrand
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 handle
n/a
0 / 0
3
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Lib\Helper;
6use App\Models\Service;
7use Closure;
8use Illuminate\Database\Eloquent\ModelNotFoundException;
9use Illuminate\Http\Request;
10
11/**
12 * @codeCoverageIgnore
13 */
14class CheckBrand
15{
16    /**
17     * Handle an incoming request.
18     *
19     * @param  \Illuminate\Http\Request $request
20     * @param  \Closure                 $next
21     * @return mixed
22     */
23    public function handle(Request $request, Closure $next)
24    {
25        $apiToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token ?? '';
26
27        if ($apiToken === null) {
28            abort(401, 'Le token d\'authentification est incorrect ' . \json_encode($request->all()));
29        }
30
31        try {
32            $brand = Service::getFromBearerToken($apiToken)->brand;
33            $request->request->add(['brand_id' => $brand->id]);
34        } catch (ModelNotFoundException $e) {
35            abort(401, 'Le token d\'authentification est incorrect ' . $e->getMessage());
36        }
37
38        return $next($request);
39    }
40}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/EncryptCookies.php.html b/tests/coverage/app/Http/Middleware/EncryptCookies.php.html new file mode 100644 index 0000000..e26acd7 --- /dev/null +++ b/tests/coverage/app/Http/Middleware/EncryptCookies.php.html @@ -0,0 +1,118 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/EncryptCookies.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
EncryptCookies
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
6
7class EncryptCookies extends Middleware
8{
9    /**
10     * The names of the cookies that should not be encrypted.
11     *
12     * @var array
13     */
14    protected $except = [];
15}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/JsonOnly.php.html b/tests/coverage/app/Http/Middleware/JsonOnly.php.html new file mode 100644 index 0000000..b67f5e8 --- /dev/null +++ b/tests/coverage/app/Http/Middleware/JsonOnly.php.html @@ -0,0 +1,185 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/JsonOnly.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
JsonOnly
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
 handle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Lib\Helper;
6use Illuminate\Http\Request;
7
8class JsonOnly
9{
10    /**
11     * Handle an incoming request.
12     *
13     * @param  \Illuminate\Http\Request $request
14     * @param  \Closure                 $next
15     * @return mixed
16     */
17    public function handle(Request $request, \Closure $next)
18    {
19        // TODO remove condition v1 check when dropped.
20        if (
21            !Helper::isRequestForApiV1($request) and
22            (!$request->isJson() or
23                !$request->expectsJson() or
24                is_null(json_decode($request->getContent())))
25        ) {
26            abort(400, 'Invalid JSON');
27        }
28
29        return $next($request);
30    }
31}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/PreventRequestsDuringMaintenance.php.html b/tests/coverage/app/Http/Middleware/PreventRequestsDuringMaintenance.php.html new file mode 100644 index 0000000..4e16d4a --- /dev/null +++ b/tests/coverage/app/Http/Middleware/PreventRequestsDuringMaintenance.php.html @@ -0,0 +1,118 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/PreventRequestsDuringMaintenance.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
PreventRequestsDuringMaintenance
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
6
7class PreventRequestsDuringMaintenance extends Middleware
8{
9    /**
10     * The URIs that should be reachable while maintenance mode is enabled.
11     *
12     * @var array
13     */
14    protected $except = [];
15}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/RedirectIfAuthenticated.php.html b/tests/coverage/app/Http/Middleware/RedirectIfAuthenticated.php.html new file mode 100644 index 0000000..a8a0326 --- /dev/null +++ b/tests/coverage/app/Http/Middleware/RedirectIfAuthenticated.php.html @@ -0,0 +1,149 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/RedirectIfAuthenticated.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
RedirectIfAuthenticated
n/a
0 / 0
n/a
0 / 0
4
n/a
0 / 0
 handle
n/a
0 / 0
4
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use App\Providers\RouteServiceProvider;
6use Closure;
7use Illuminate\Http\Request;
8use Illuminate\Support\Facades\Auth;
9
10/**
11 * @codeCoverageIgnore
12 */
13class RedirectIfAuthenticated
14{
15    /**
16     * Handle an incoming request.
17     *
18     * @param  \Illuminate\Http\Request $request
19     * @param  \Closure                 $next
20     * @param  string|null              ...$guards
21     * @return mixed
22     */
23    public function handle(Request $request, Closure $next, ...$guards)
24    {
25        $guards = empty($guards) ? [null] : $guards;
26
27        foreach ($guards as $guard) {
28            if (Auth::guard($guard)->check()) {
29                return redirect(RouteServiceProvider::HOME);
30            }
31        }
32
33        return $next($request);
34    }
35}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/TrimStrings.php.html b/tests/coverage/app/Http/Middleware/TrimStrings.php.html new file mode 100644 index 0000000..54ed2a1 --- /dev/null +++ b/tests/coverage/app/Http/Middleware/TrimStrings.php.html @@ -0,0 +1,121 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/TrimStrings.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
TrimStrings
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
6
7class TrimStrings extends Middleware
8{
9    /**
10     * The names of the attributes that should not be trimmed.
11     *
12     * @var array
13     */
14    protected $except = [
15        'password',
16        'password_confirmation',
17    ];
18}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/TrustHosts.php.html b/tests/coverage/app/Http/Middleware/TrustHosts.php.html new file mode 100644 index 0000000..e699d83 --- /dev/null +++ b/tests/coverage/app/Http/Middleware/TrustHosts.php.html @@ -0,0 +1,137 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/TrustHosts.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
TrustHosts
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 hosts
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Illuminate\Http\Middleware\TrustHosts as Middleware;
6
7/**
8 * @codeCoverageIgnore
9 */
10class TrustHosts extends Middleware
11{
12    /**
13     * Get the host patterns that should be trusted.
14     *
15     * @return array
16     */
17    public function hosts()
18    {
19        return [
20            $this->allSubdomainsOfApplicationUrl(),
21        ];
22    }
23}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/TrustProxies.php.html b/tests/coverage/app/Http/Middleware/TrustProxies.php.html new file mode 100644 index 0000000..0d1b0b3 --- /dev/null +++ b/tests/coverage/app/Http/Middleware/TrustProxies.php.html @@ -0,0 +1,130 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/TrustProxies.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
TrustProxies
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Fideloper\Proxy\TrustProxies as Middleware;
6use Illuminate\Http\Request;
7
8class TrustProxies extends Middleware
9{
10    /**
11     * The trusted proxies for this application.
12     *
13     * @var array|string|null
14     */
15    protected $proxies;
16
17    /**
18     * The headers that should be used to detect proxies.
19     *
20     * @var integer
21     */
22    protected $headers = Request::HEADER_X_FORWARDED_FOR |
23    Request::HEADER_X_FORWARDED_HOST |
24    Request::HEADER_X_FORWARDED_PORT |
25    Request::HEADER_X_FORWARDED_PROTO |
26    Request::HEADER_X_FORWARDED_AWS_ELB;
27}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/VerifyCsrfToken.php.html b/tests/coverage/app/Http/Middleware/VerifyCsrfToken.php.html new file mode 100644 index 0000000..b957589 --- /dev/null +++ b/tests/coverage/app/Http/Middleware/VerifyCsrfToken.php.html @@ -0,0 +1,118 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/VerifyCsrfToken.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
VerifyCsrfToken
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
6
7class VerifyCsrfToken extends Middleware
8{
9    /**
10     * The URIs that should be excluded from CSRF verification.
11     *
12     * @var array
13     */
14    protected $except = [];
15}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/XssSanitizer.php.html b/tests/coverage/app/Http/Middleware/XssSanitizer.php.html new file mode 100644 index 0000000..7bd6901 --- /dev/null +++ b/tests/coverage/app/Http/Middleware/XssSanitizer.php.html @@ -0,0 +1,179 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware/XssSanitizer.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
XssSanitizer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
 handle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Middleware;
4
5use Closure;
6
7class XssSanitizer
8{
9    /**
10     * Handle an incoming request.
11     *
12     * @param  mixed    $request
13     * @param  \Closure $next
14     * @return mixed
15     */
16    public function handle($request, Closure $next)
17    {
18        $input = $request->all();
19        array_walk_recursive($input, function (&$input) {
20            $input = strip_tags($input);
21        });
22        $request->merge($input);
23        return $next($request);
24    }
25}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/dashboard.html b/tests/coverage/app/Http/Middleware/dashboard.html new file mode 100644 index 0000000..6395d5a --- /dev/null +++ b/tests/coverage/app/Http/Middleware/dashboard.html @@ -0,0 +1,286 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http/Middleware + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/Middleware/index.html b/tests/coverage/app/Http/Middleware/index.html new file mode 100644 index 0000000..f08f24d --- /dev/null +++ b/tests/coverage/app/Http/Middleware/index.html @@ -0,0 +1,337 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Middleware + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
37 / 37
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
ApiCheckTimeStamps.php
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiResponse.php
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Authenticate.php
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CheckBrand.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
EncryptCookies.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
JsonOnly.php
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
PreventRequestsDuringMaintenance.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
RedirectIfAuthenticated.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
TrimStrings.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
TrustHosts.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
TrustProxies.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
VerifyCsrfToken.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
XssSanitizer.php
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/app/Http/Requests/Api/V2/AuthenticateRequest.php.html b/tests/coverage/app/Http/Requests/Api/V2/AuthenticateRequest.php.html new file mode 100644 index 0000000..4fd7aae --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/AuthenticateRequest.php.html @@ -0,0 +1,206 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2/AuthenticateRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
AuthenticateRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 authorize
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5class AuthenticateRequest extends BaseApiRequest
6{
7    /**
8     * Determine if the user is authorized to make this request.
9     *
10     * @return boolean
11     */
12    public function authorize()
13    {
14        return true;
15    }
16
17    /**
18     * Get the validation rules that apply to the request.
19     *
20     * @return array
21     */
22    public function rules(): array
23    {
24        return [
25            'email' => 'required|email:rfc',
26            'password' => 'required'
27        ];
28    }
29}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/V2/BaseApiRequest.php.html b/tests/coverage/app/Http/Requests/Api/V2/BaseApiRequest.php.html new file mode 100644 index 0000000..0e1a995 --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/BaseApiRequest.php.html @@ -0,0 +1,216 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2/BaseApiRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
BaseApiRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 authorize
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 rules
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use ArondeParon\RequestSanitizer\Sanitizers\Capitalize;
6use ArondeParon\RequestSanitizer\Sanitizers\Lowercase;
7use ArondeParon\RequestSanitizer\Sanitizers\RemoveNonNumeric;
8use ArondeParon\RequestSanitizer\Sanitizers\Trim;
9use ArondeParon\RequestSanitizer\Sanitizers\TrimDuplicateSpaces;
10use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
11use Illuminate\Foundation\Http\FormRequest;
12
13abstract class BaseApiRequest extends FormRequest
14{
15    use SanitizesInputs;
16
17    /**
18     * Using these sanitizers before proceeding the request
19     * @var array
20     */
21    protected $sanitizers = [
22        'firstname' => [Trim::class, TrimDuplicateSpaces::class, Capitalize::class],
23        'lastname' => [Trim::class, TrimDuplicateSpaces::class, Capitalize::class],
24        'address' => [Trim::class, TrimDuplicateSpaces::class],
25        'address2' => [Trim::class, TrimDuplicateSpaces::class],
26        'city' => [Trim::class, TrimDuplicateSpaces::class],
27        'password' => Trim::class,
28        'phone' => Trim::class,
29        'email' => [Lowercase::class, Trim::class],
30        'postcode' => [RemoveNonNumeric::class, Trim::class]
31    ];
32
33    /**
34     * Determine if the user is authorized to make this request.
35     *
36     * @return boolean
37     */
38    public function authorize()
39    {
40        return auth()->check();
41    }
42
43    /**
44     * Get the validation rules that apply to the request.
45     *
46     * @return array
47     */
48    abstract public function rules(): array;
49}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/V2/GetBrandStatsRequest.php.html b/tests/coverage/app/Http/Requests/Api/V2/GetBrandStatsRequest.php.html new file mode 100644 index 0000000..0631352 --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/GetBrandStatsRequest.php.html @@ -0,0 +1,195 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2/GetBrandStatsRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
GetBrandStatsRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Http\Requests\Api\V2\BaseApiRequest;
6use Illuminate\Support\Carbon;
7use Illuminate\Validation\ValidationException;
8
9class GetBrandStatsRequest extends BaseApiRequest
10{
11    /**
12     * Get the validation rules that apply to the request.
13     *
14     * @return array
15     * @throws ValidationException If given dates are unparsable of period is bigger than 3 months.
16     * @throws \Exception If carbon date processing failed.
17     */
18    public function rules(): array
19    {
20        try {
21            if (Carbon::parse($this->end)->diffInMonths(Carbon::parse($this->start)->addDay()) >= 3) {
22                throw ValidationException::withMessages([
23                    'Vous devez sélectionner une période de maximum 3 mois'
24                ]);
25            }
26        } catch (\Exception $e) {
27            if (\get_class($e) !== ValidationException::class) {
28                throw ValidationException::withMessages([
29                    'Les dates séléectionnées ne sembles pas être valides'
30                ]);
31            }
32            throw $e;
33        }
34        return [
35            'start' => 'required|date|date_format:Y-m-d',
36            'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
37        ];
38    }
39}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/V2/GetQuestionStatsRequest.php.html b/tests/coverage/app/Http/Requests/Api/V2/GetQuestionStatsRequest.php.html new file mode 100644 index 0000000..187eb9d --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/GetQuestionStatsRequest.php.html @@ -0,0 +1,196 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2/GetQuestionStatsRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
GetQuestionStatsRequest
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
4.18
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
 rules
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
4.18
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Http\Requests\Api\V2\BaseApiRequest;
6use Exception;
7use Illuminate\Support\Carbon;
8use Illuminate\Validation\ValidationException;
9
10class GetQuestionStatsRequest extends BaseApiRequest
11{
12    /**
13     * Get the validation rules that apply to the request.
14     *
15     * @return array
16     * @throws ValidationException If given dates are unparsable of period is bigger than 3 months.
17     * @throws \Exception If carbon date processing failed.
18     */
19    public function rules(): array
20    {
21        try {
22            if (Carbon::parse($this->end)->diffInMonths(Carbon::parse($this->start)->addDay()) >= 3) {
23                throw ValidationException::withMessages([
24                    'Vous devez sélectionner une période de maximum 3 mois'
25                ]);
26            }
27        } catch (Exception $e) {
28            if (\get_class($e) !== ValidationException::class) {
29                throw ValidationException::withMessages([
30                    'Les dates sélectionnées ne sembles pas être valides'
31                ]);
32            }
33            throw $e;
34        }
35        return [
36            'start' => 'required|date|date_format:Y-m-d',
37            'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
38        ];
39    }
40}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php.html b/tests/coverage/app/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php.html new file mode 100644 index 0000000..a3a8448 --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php.html @@ -0,0 +1,305 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2/SendArchadiaDefaultMailRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
35 / 35
SendArchadiaDefaultMailRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
35 / 35
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
17 / 17
 attributes
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
16 / 16
 messages
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Enums\ArcadiaOrderType;
6use ArondeParon\RequestSanitizer\Sanitizers\RemoveNonNumeric;
7use ArondeParon\RequestSanitizer\Sanitizers\Trim;
8use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
9use Kwaadpepper\Enum\Rules\EnumIsValidRule;
10
11class SendArchadiaDefaultMailRequest extends BaseApiRequest
12{
13    use SanitizesInputs;
14
15    /**
16     * Using these sanitizers before proceeding the request
17     * @var array
18     */
19    protected $sanitizers = [
20        'label_type_support' => Trim::class,
21        'operation' => Trim::class,
22        'order_date' => Trim::class,
23        'order_num' => RemoveNonNumeric::class,
24        'package_unit_weight' => RemoveNonNumeric::class,
25        'support_code' => Trim::class,
26        'deliver.firstname' => Trim::class,
27        'deliver.lastname' => Trim::class,
28        'deliver.email' => Trim::class,
29        'deliver.address' => Trim::class,
30        'deliver.address2' => Trim::class,
31        'deliver.city' => Trim::class,
32        'deliver.postcode' => Trim::class,
33        'deliver.phone' => Trim::class
34    ];
35
36    /**
37     * Get the validation rules that apply to the request.
38     *
39     * @return array
40     */
41    public function rules(): array
42    {
43        return [
44            'label_type_support' => 'required|string',
45            'operation' => 'required|int',
46            'order_date' => 'required|date',
47            'order_num' => 'required|numeric',
48            'package_unit_weight' => 'required|integer',
49            'state_order' => [
50                'required',
51                new EnumIsValidRule(ArcadiaOrderType::class)
52            ],
53            'support_code' => 'required|int',
54            'deliver' => 'required|array',
55            'deliver.firstname' => 'required|string|min:1|max:255',
56            'deliver.lastname' => 'required|string|min:1|max:255',
57            'deliver.email' => 'required|email:rfc,dns,spoof,filter|max:255',
58            'deliver.address' => 'required|string|min:2|max:255',
59            'deliver.address2' => 'sometimes|string|min:2|max:255',
60            'deliver.city' => 'required|string|min:2|max:255',
61            'deliver.postcode' => 'required|string|postal_code:FR|max:255',
62            'deliver.phone' => 'required|string|phone:FR|max:255',
63        ];
64    }
65
66    /**
67     * Attributes
68     *
69     * @return array
70     */
71    public function attributes()
72    {
73        return [
74            'label_type_support' => trans('label type support'),
75            'operation' => trans('code opération'),
76            'order_date' => trans('date de la commande'),
77            'order_num' => trans('numero de commande'),
78            'package_unit_weight' => trans('poids unitaire du paquet'),
79            'state_order' => trans('type de commande arcadia'),
80            'support_code' => trans('code de support'),
81            'deliver' => trans('coordonées de livraison'),
82            'deliver.civility' => trans('civilitée dans coordonées de livraison'),
83            'deliver.firstname' => trans('prénom dans coordonées de livraison'),
84            'deliver.lastname' => trans('nom dans coordonées de livraison'),
85            'deliver.address' => trans('adresse dans coordonées de livraison'),
86            'deliver.postcode' => trans('code postal dans coordonées de livraison'),
87            'deliver.phone' => trans('téléphone dans coordonées de livraison'),
88            'deliver.city' => trans('ville dans coordonées de livraison'),
89            'deliver.email' => trans('e-mail dans coordonées de livraison'),
90        ];
91    }
92
93    /**
94     * Messages
95     *
96     * @return array
97     */
98    public function messages()
99    {
100        return [
101            // phpcs:ignore Generic.Files.LineLength.TooLong
102            'deliver.phone.phone' => trans('Le champ :attribute est invalide, il accepte les numéros des pays suivants: France.'),
103            // phpcs:ignore Generic.Files.LineLength.TooLong
104            'deliver.postcode.postal_code' => trans('Le champ :attribute accepte les codes postaux des pays suivants :countries comme par exemple :examples.'),
105        ];
106    }
107}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php.html b/tests/coverage/app/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php.html new file mode 100644 index 0000000..583e07c --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php.html @@ -0,0 +1,298 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2/SendArchadiaGameMailRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
SendArchadiaGameMailRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 attributes
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
 messages
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use ArondeParon\RequestSanitizer\Sanitizers\Trim;
6use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
7
8class SendArchadiaGameMailRequest extends BaseApiRequest
9{
10    use SanitizesInputs;
11
12    /**
13     * Using these sanitizers before proceeding the request
14     * @var array
15     */
16    protected $sanitizers = [
17        'code_on_pack' => Trim::class,
18        'code_unique' => Trim::class,
19        'label_type_support' => Trim::class,
20        'operation' => Trim::class,
21        'order_date' => Trim::class,
22        'support_code' => Trim::class,
23        'type' => Trim::class,
24        'deliver.firstname' => Trim::class,
25        'deliver.lastname' => Trim::class,
26        'deliver.email' => Trim::class,
27        'deliver.address' => Trim::class,
28        'deliver.address2' => Trim::class,
29        'deliver.city' => Trim::class,
30        'deliver.postcode' => Trim::class,
31        'deliver.phone' => Trim::class
32    ];
33
34    /**
35     * Get the validation rules that apply to the request.
36     *
37     * @return array
38     */
39    public function rules(): array
40    {
41        return [
42            'code_on_pack' => 'sometimes|string',
43            'code_unique' => 'required|string',
44            'label_type_support' => 'required|string',
45            'operation' => 'required|int',
46            'order_date' => 'required|date',
47            'support_code' => 'required|int',
48            'deliver' => 'required|array',
49            'deliver.firstname' => 'required|string|min:1|max:255',
50            'deliver.lastname' => 'required|string|min:1|max:255',
51            'deliver.email' => 'required|email:rfc,dns,spoof,filter|max:255',
52            'deliver.address' => 'required|string|min:2|max:255',
53            'deliver.address2' => 'sometimes|string|min:2|max:255',
54            'deliver.city' => 'required|string|min:2|max:255',
55            'deliver.postcode' => 'required|string|postal_code:FR|max:255',
56            'deliver.phone' => 'required|string|phone:FR|max:255',
57        ];
58    }
59
60    /**
61     * Attributes
62     *
63     * @return array
64     */
65    public function attributes()
66    {
67        return [
68            'code_on_pack' => trans('code ean emballage'),
69            'code_unique' => trans('code unique'),
70            'label_type_support' => trans('label type support'),
71            'operation' => trans('code opération'),
72            'order_date' => trans('date de la commande'),
73            'support_code' => trans('code de support'),
74            'deliver' => trans('coordonées de livraison'),
75            'deliver.civility' => trans('civilitée dans coordonées de livraison'),
76            'deliver.firstname' => trans('prénom dans coordonées de livraison'),
77            'deliver.lastname' => trans('nom dans coordonées de livraison'),
78            'deliver.address' => trans('adresse dans coordonées de livraison'),
79            'deliver.postcode' => trans('code postal dans coordonées de livraison'),
80            'deliver.phone' => trans('téléphone dans coordonées de livraison'),
81            'deliver.city' => trans('ville dans coordonées de livraison'),
82            'deliver.email' => trans('e-mail dans coordonées de livraison'),
83        ];
84    }
85
86    /**
87     * Messages
88     *
89     * @return array
90     */
91    public function messages()
92    {
93        return [
94            // phpcs:ignore Generic.Files.LineLength.TooLong
95            'deliver.phone.phone' => trans('Le champ :attribute est invalide, il accepte les numéros des pays suivants: France.'),
96            // phpcs:ignore Generic.Files.LineLength.TooLong
97            'deliver.postcode.postal_code' => trans('Le champ :attribute accepte les codes postaux des pays suivants :countries comme par exemple :examples.'),
98        ];
99    }
100}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/V2/SingleOptinRequest.php.html b/tests/coverage/app/Http/Requests/Api/V2/SingleOptinRequest.php.html new file mode 100644 index 0000000..d3586af --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/SingleOptinRequest.php.html @@ -0,0 +1,175 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2/SingleOptinRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
SingleOptinRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5class SingleOptinRequest extends BaseApiRequest
6{
7    /**
8     * Get the validation rules that apply to the request.
9     *
10     * @return array
11     */
12    public function rules(): array
13    {
14        return [
15            'email' => 'required|email:rfc,dns,spoof,filter',
16            'opt_in' => 'required|boolean'
17        ];
18    }
19}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/V2/StoreCustomerAnswers.php.html b/tests/coverage/app/Http/Requests/Api/V2/StoreCustomerAnswers.php.html new file mode 100644 index 0000000..298f32a --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/StoreCustomerAnswers.php.html @@ -0,0 +1,203 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2/StoreCustomerAnswers.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 89.47% covered (warning) +
+
+
89.47%
17 / 19
StoreCustomerAnswers
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.01
+
+ 89.47% covered (warning) +
+
+
89.47%
17 / 19
 rules
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.01
+
+ 89.47% covered (warning) +
+
+
89.47%
17 / 19
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Models\Answer;
6use App\Models\Question;
7
8class StoreCustomerAnswers extends BaseApiRequest
9{
10    /**
11     * Get the validation rules that apply to the request.
12     *
13     * @return array
14     */
15    public function rules(): array
16    {
17        // * Resolve Service.
18        $service           = app()->make(\App\Models\Service::class);
19        $brandId           = $service->brand->id;
20        $question          = Question::findOrFail($this->question);
21        $questionTableName = (new Question())->getTable();
22        $answerTableName   = (new Answer())->getTable();
23        return [
24            'question' => [
25                'required',
26                "exists:$questionTableName,id",
27                // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
28                function ($attribute, $value, $fail) use ($question, $brandId) {
29                    if ($question->brand->id !== $brandId) {
30                        $fail("$attribute does not belong to brand $brandId");
31                    }
32                }
33            ],
34            'answers' => 'required|array',
35            'answers.*' => [
36                'int',
37                'distinct',
38                "exists:$answerTableName,id",
39                function ($attribute, $value, $fail) use ($question) {
40                    if (Answer::findOrFail($value)->question->id !== $question->id) {
41                        $fail("$attribute does not belong to question $question->id");
42                    }
43                }
44            ]
45        ];
46    }
47}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/V2/StoreCustomerRequest.php.html b/tests/coverage/app/Http/Requests/Api/V2/StoreCustomerRequest.php.html new file mode 100644 index 0000000..3e32529 --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/StoreCustomerRequest.php.html @@ -0,0 +1,258 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2/StoreCustomerRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
40 / 40
StoreCustomerRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
40 / 40
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
38 / 38
 messages
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Enums\Country;
6use Illuminate\Validation\Rules\Password;
7use Kwaadpepper\Enum\Rules\EnumIsValidRule;
8
9class StoreCustomerRequest extends BaseApiRequest
10{
11    /**
12     * Get the validation rules that apply to the request.
13     *
14     * @return array
15     */
16    public function rules(): array
17    {
18        return [
19            'firstname' => 'required|string|min:1|max:255',
20            'lastname' => 'required|string|min:1|max:255',
21            'email' => 'required|email:rfc,dns,spoof,filter|max:255',
22            'password' => [
23                'required',
24                'max:255',
25                Password::min(8)
26                    ->mixedCase()
27                    ->letters()
28                    ->numbers()
29                    ->symbols()
30                    ->uncompromised()
31            ],
32            'birth_date' => 'required|date_format:Y-m-d|max:255',
33            'address' => 'sometimes|required_with:address2|string|max:255',
34            'address2' => 'sometimes|string|max:255',
35            'phone' => 'sometimes|string|phone:FR,BE,LU',
36            'country' => [
37                'sometimes',
38                'numeric',
39                new EnumIsValidRule(Country::class),
40                sprintf(
41                    'in:%s',
42                    implode(',', [
43                        Country::belgium()->value,
44                        Country::luxembourg()->value,
45                        Country::france()->value,
46                        // * DROM COM.
47                        Country::guadeloupe()->value,
48                        Country::martinique()->value,
49                        Country::frenchGuiana()->value,
50                        Country::reunion()->value,
51                        Country::saintPierreAndMiquelon()->value,
52                        Country::mayotte()->value,
53                        Country::saintBarthelemy()->value,
54                        Country::saintMartinFrenchPart()->value,
55                        Country::wallisAndFutuna()->value,
56                        Country::frenchPolynesia()->value,
57                        Country::newCaledonia()->value
58                    ])
59                )
60            ],
61            'city' => 'sometimes|string|min:2|max:255',
62            'postcode' => 'sometimes|string|postal_code:FR,BE,LU',
63            'opt_in' => 'required|boolean',
64        ];
65    }
66
67    /**
68     * Messages
69     *
70     * @return array
71     */
72    public function messages()
73    {
74        return [
75            // phpcs:ignore Generic.Files.LineLength.TooLong
76            'phone.phone' => trans('Le champ :attribute est invalide, il accepte les numéros des pays suivants France, Belgique, Luxembourg.'),
77            // phpcs:ignore Generic.Files.LineLength.TooLong
78            'country.in' => trans('Le champ :attribute est invalide, il accepte les pays suivants France, Belgique, Luxembourg.')
79        ];
80    }
81}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/V2/UpdateCustomerRequest.php.html b/tests/coverage/app/Http/Requests/Api/V2/UpdateCustomerRequest.php.html new file mode 100644 index 0000000..fb62fa0 --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/UpdateCustomerRequest.php.html @@ -0,0 +1,267 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2/UpdateCustomerRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
44 / 44
UpdateCustomerRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
44 / 44
 rules
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
42 / 42
 messages
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests\Api\V2;
4
5use App\Enums\Country;
6use Illuminate\Validation\Rules\Password;
7use Kwaadpepper\Enum\Rules\EnumIsValidRule;
8
9class UpdateCustomerRequest extends BaseApiRequest
10{
11    /**
12     * Get the validation rules that apply to the request.
13     *
14     * @return array
15     */
16    public function rules(): array
17    {
18        $fields  = [
19            'firstname', 'lastname', 'email', 'password',
20            'birth_date', 'address', 'phone', 'country',
21            'city', 'postcode', 'opt_in'
22        ];
23        $atLeast = function ($attribute) use ($fields) {
24            return 'required_without_all:' . collect($fields)
25                ->filter(function ($fieldName) use ($attribute) {
26                    return $fieldName !== $attribute;
27                })->implode(',');
28        };
29        return [
30            'firstname' => [$atLeast('firstname'), 'string', 'min:1', 'max:255'],
31            'lastname' => [$atLeast('lastname'), 'string', 'min:1', 'max:255'],
32            'email' => [$atLeast('email'), 'email:rfc,dns,spoof,filter', 'max:255'],
33            'password' => [
34                $atLeast('password'), 'max:255', Password::min(8)
35                    ->mixedCase()
36                    ->letters()
37                    ->numbers()
38                    ->symbols()
39                    ->uncompromised()
40            ],
41            'birth_date' => [$atLeast('birth_date'), 'date_format:Y-m-d', 'max:255'],
42            'address' => [$atLeast('address'), 'required_with:address2', 'string', 'max:255'],
43            'address2' => ['sometimes', 'string', 'max:255'],
44            'phone' => [$atLeast('phone'), 'string', 'phone:FR,BE,LU', 'max:255'],
45            'country' => [
46                $atLeast('country'),
47                'numeric',
48                new EnumIsValidRule(Country::class),
49                sprintf(
50                    'in:%s',
51                    implode(',', [
52                        Country::belgium()->value,
53                        Country::luxembourg()->value,
54                        Country::france()->value,
55                        // * DROM COM.
56                        Country::guadeloupe()->value,
57                        Country::martinique()->value,
58                        Country::frenchGuiana()->value,
59                        Country::reunion()->value,
60                        Country::saintPierreAndMiquelon()->value,
61                        Country::mayotte()->value,
62                        Country::saintBarthelemy()->value,
63                        Country::saintMartinFrenchPart()->value,
64                        Country::wallisAndFutuna()->value,
65                        Country::frenchPolynesia()->value,
66                        Country::newCaledonia()->value
67                    ])
68                )
69            ],
70            'city' => [$atLeast('city'), 'string', 'min:2', 'max:255'],
71            'postcode' => [$atLeast('postcode'), 'string', 'postal_code:FR,BE,LU', 'max:255'],
72            'opt_in' => [$atLeast('opt_in'), 'boolean'],
73        ];
74    }
75
76    /**
77     * Messages
78     *
79     * @return array
80     */
81    public function messages()
82    {
83        return [
84            // phpcs:ignore Generic.Files.LineLength.TooLong
85            'phone.phone' => trans('Le champ :attribute est invalide, il accepte les numéros des pays suivants France, Belgique, Luxembourg.'),
86            // phpcs:ignore Generic.Files.LineLength.TooLong
87            'country.in' => trans('Le champ :attribute est invalide, il accepte les pays suivants France, Belgique, Luxembourg.')
88        ];
89    }
90}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/V2/dashboard.html b/tests/coverage/app/Http/Requests/Api/V2/dashboard.html new file mode 100644 index 0000000..4fbc2d1 --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/dashboard.html @@ -0,0 +1,296 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+ +
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
rules77%
rules89%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + +
MethodCRAP
rules4
rules3
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/V2/index.html b/tests/coverage/app/Http/Requests/Api/V2/index.html new file mode 100644 index 0000000..9fe3377 --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/V2/index.html @@ -0,0 +1,375 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
AuthenticateRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
BaseApiRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
GetBrandStatsRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
GetQuestionStatsRequest.php
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
SendArchadiaDefaultMailRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
35 / 35
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
SendArchadiaGameMailRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
SingleOptinRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
StoreCustomerAnswers.php
+
+ 89.47% covered (warning) +
+
+
89.47%
17 / 19
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
StoreCustomerRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
40 / 40
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
UpdateCustomerRequest.php
+
+ 100.00% covered (success) +
+
+
100.00%
44 / 44
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/app/Http/Requests/Api/dashboard.html b/tests/coverage/app/Http/Requests/Api/dashboard.html new file mode 100644 index 0000000..8f63d51 --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/dashboard.html @@ -0,0 +1,295 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http/Requests/Api + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+ +
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
rules77%
rules89%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + +
MethodCRAP
rules4
rules3
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/Api/index.html b/tests/coverage/app/Http/Requests/Api/index.html new file mode 100644 index 0000000..ded0047 --- /dev/null +++ b/tests/coverage/app/Http/Requests/Api/index.html @@ -0,0 +1,122 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/Api + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
V2
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ +
+ + diff --git a/tests/coverage/app/Http/Requests/CustomerExport.php.html b/tests/coverage/app/Http/Requests/CustomerExport.php.html new file mode 100644 index 0000000..eb7de77 --- /dev/null +++ b/tests/coverage/app/Http/Requests/CustomerExport.php.html @@ -0,0 +1,169 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/CustomerExport.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerExport
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use App\Enums\CustomerExportType;
6use Illuminate\Foundation\Http\FormRequest;
7
8/**
9 * @codeCoverageIgnore
10 */
11class CustomerExport extends FormRequest
12{
13    /**
14     * Determine if the user is authorized to make this request.
15     *
16     * @return boolean
17     */
18    public function authorize()
19    {
20        return auth()->check();
21    }
22
23    /**
24     * Get the validation rules that apply to the request.
25     *
26     * @return array
27     */
28    public function rules()
29    {
30        return [
31            'brand' => 'required|numeric|exists:brands,id',
32            'export_type' => [
33                'required',
34                'numeric',
35                // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
36                function ($attribute, $value, $fail) {
37                    if (!\in_array($value, CustomerExportType::values())) {
38                        $fail(trans('Le type d\'export invalide'));
39                    }
40                }
41            ]
42        ];
43    }
44}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/GenerateServiceToken.php.html b/tests/coverage/app/Http/Requests/GenerateServiceToken.php.html new file mode 100644 index 0000000..2be8893 --- /dev/null +++ b/tests/coverage/app/Http/Requests/GenerateServiceToken.php.html @@ -0,0 +1,156 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/GenerateServiceToken.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
GenerateServiceToken
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use Illuminate\Foundation\Http\FormRequest;
6
7/**
8 * @codeCoverageIgnore
9 */
10class GenerateServiceToken extends FormRequest
11{
12    /**
13     * Determine if the user is authorized to make this request.
14     *
15     * @return boolean
16     */
17    public function authorize()
18    {
19        return auth()->check();
20    }
21
22    /**
23     * Get the validation rules that apply to the request.
24     *
25     * @return array
26     */
27    public function rules()
28    {
29        return [];
30    }
31}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/ManageBrandRequest.php.html b/tests/coverage/app/Http/Requests/ManageBrandRequest.php.html new file mode 100644 index 0000000..a1a1645 --- /dev/null +++ b/tests/coverage/app/Http/Requests/ManageBrandRequest.php.html @@ -0,0 +1,177 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/ManageBrandRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ManageBrandRequest
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use ArondeParon\RequestSanitizer\Sanitizers\FilterVars;
6use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
7use Illuminate\Foundation\Http\FormRequest;
8
9/**
10 * @codeCoverageIgnore
11 */
12class ManageBrandRequest extends FormRequest
13{
14    use SanitizesInputs;
15
16    /**
17     * Using these sanitizers before proceeding the request
18     * @var array
19     */
20    protected $sanitizers = [
21        'name' => [
22            FilterVars::class => [
23                'filter' => FILTER_SANITIZE_STRING
24            ]
25        ],
26    ];
27
28    /**
29     * Determine if the user is authorized to make this request.
30     *
31     * @return boolean
32     */
33    public function authorize()
34    {
35        return auth()->check();
36    }
37
38    /**
39     * Get the validation rules that apply to the request.
40     *
41     * @return array
42     */
43    public function rules()
44    {
45        if ($this->isMethod('post')) {
46            return [
47                'name' => 'required|string|min:5|max:255'
48            ];
49        }
50        return [];
51    }
52}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/ManageServiceRequest.php.html b/tests/coverage/app/Http/Requests/ManageServiceRequest.php.html new file mode 100644 index 0000000..37fc69f --- /dev/null +++ b/tests/coverage/app/Http/Requests/ManageServiceRequest.php.html @@ -0,0 +1,178 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/ManageServiceRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ManageServiceRequest
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use ArondeParon\RequestSanitizer\Sanitizers\FilterVars;
6use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
7use Illuminate\Foundation\Http\FormRequest;
8
9/**
10 * @codeCoverageIgnore
11 */
12class ManageServiceRequest extends FormRequest
13{
14    use SanitizesInputs;
15
16    /**
17     * Using these sanitizers before proceeding the request
18     * @var array
19     */
20    protected $sanitizers = [
21        'name' => [
22            FilterVars::class => [
23                'filter' => FILTER_SANITIZE_STRING
24            ]
25        ],
26    ];
27
28    /**
29     * Determine if the user is authorized to make this request.
30     *
31     * @return boolean
32     */
33    public function authorize()
34    {
35        return auth()->check();
36    }
37
38    /**
39     * Get the validation rules that apply to the request.
40     *
41     * @return array
42     */
43    public function rules()
44    {
45        if ($this->isMethod('post')) {
46            return [
47                'name' => 'required|string|min:5|max:255',
48                'brand_id' => 'required|exists:brands,id'
49            ];
50        }
51        return [];
52    }
53}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/QuestionExport.php.html b/tests/coverage/app/Http/Requests/QuestionExport.php.html new file mode 100644 index 0000000..846d427 --- /dev/null +++ b/tests/coverage/app/Http/Requests/QuestionExport.php.html @@ -0,0 +1,158 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/QuestionExport.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
QuestionExport
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use Illuminate\Foundation\Http\FormRequest;
6
7/**
8 * @codeCoverageIgnore
9 */
10class QuestionExport extends FormRequest
11{
12    /**
13     * Determine if the user is authorized to make this request.
14     *
15     * @return boolean
16     */
17    public function authorize()
18    {
19        return auth()->check();
20    }
21
22    /**
23     * Get the validation rules that apply to the request.
24     *
25     * @return array
26     */
27    public function rules()
28    {
29        return [
30            'brand' => 'required|numeric|exists:brands,id'
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/StatsByBrandRequest.php.html b/tests/coverage/app/Http/Requests/StatsByBrandRequest.php.html new file mode 100644 index 0000000..3d2f155 --- /dev/null +++ b/tests/coverage/app/Http/Requests/StatsByBrandRequest.php.html @@ -0,0 +1,182 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/StatsByBrandRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StatsByBrandRequest
n/a
0 / 0
n/a
0 / 0
6
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
5
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use Exception;
6use Illuminate\Foundation\Http\FormRequest;
7use Illuminate\Support\Carbon;
8use Illuminate\Validation\ValidationException;
9
10/**
11 * @codeCoverageIgnore
12 */
13class StatsByBrandRequest extends FormRequest
14{
15    /**
16     * Determine if the user is authorized to make this request.
17     *
18     * @return boolean
19     */
20    public function authorize()
21    {
22        return auth()->check();
23    }
24
25    /**
26     * Get the validation rules that apply to the request.
27     *
28     * @return array
29     * @throws ValidationException If given dates are unparsable of period is bigger than 1 year.
30     * @throws \Exception If carbon date processing failed.
31     */
32    public function rules()
33    {
34        if ($this->post()) {
35            try {
36                if ((Carbon::parse($this->end)->diffInDays(Carbon::parse($this->start)) + 1) > 365) {
37                    throw ValidationException::withMessages([
38                        'Vous devez sélectionner une période de maximum 1 ans'
39                    ]);
40                }
41            } catch (Exception $e) {
42                if (\get_class($e) !== ValidationException::class) {
43                    throw ValidationException::withMessages([
44                        'Les dates séléectionnées ne sembles pas être valides'
45                    ]);
46                }
47                throw $e;
48            }
49            return [
50                'brand' => 'required|exists:brands,id',
51                'start' => 'required|date|date_format:Y-m-d',
52                'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
53            ];
54        }//end if
55        return [];
56    }
57}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/StatsByQuestionRequest.php.html b/tests/coverage/app/Http/Requests/StatsByQuestionRequest.php.html new file mode 100644 index 0000000..0121834 --- /dev/null +++ b/tests/coverage/app/Http/Requests/StatsByQuestionRequest.php.html @@ -0,0 +1,182 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests/StatsByQuestionRequest.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StatsByQuestionRequest
n/a
0 / 0
n/a
0 / 0
6
n/a
0 / 0
 authorize
n/a
0 / 0
1
n/a
0 / 0
 rules
n/a
0 / 0
5
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Requests;
4
5use Exception;
6use Illuminate\Foundation\Http\FormRequest;
7use Illuminate\Support\Carbon;
8use Illuminate\Validation\ValidationException;
9
10/**
11 * @codeCoverageIgnore
12 */
13class StatsByQuestionRequest extends FormRequest
14{
15    /**
16     * Determine if the user is authorized to make this request.
17     *
18     * @return boolean
19     */
20    public function authorize()
21    {
22        return auth()->check();
23    }
24
25    /**
26     * Get the validation rules that apply to the request.
27     *
28     * @return array
29     * @throws ValidationException If given dates are unparsable of period is bigger than 1 year.
30     * @throws \Exception If carbon date processing failed.
31     */
32    public function rules()
33    {
34        if ($this->post()) {
35            try {
36                if ((Carbon::parse($this->end)->diffInDays(Carbon::parse($this->start)) + 1) > 365) {
37                    throw ValidationException::withMessages([
38                        'Vous devez sélectionner une période de maximum 1 ans'
39                    ]);
40                }
41            } catch (Exception $e) {
42                if (\get_class($e) !== ValidationException::class) {
43                    throw ValidationException::withMessages([
44                        'Les dates séléectionnées ne sembles pas être valides'
45                    ]);
46                }
47                throw $e;
48            }
49            return [
50                'question' => 'required|exists:questions,id',
51                'start' => 'required|date|date_format:Y-m-d',
52                'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
53            ];
54        }//end if
55        return [];
56    }
57}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/dashboard.html b/tests/coverage/app/Http/Requests/dashboard.html new file mode 100644 index 0000000..ece0009 --- /dev/null +++ b/tests/coverage/app/Http/Requests/dashboard.html @@ -0,0 +1,294 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http/Requests + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+ +
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
rules77%
rules89%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + +
MethodCRAP
rules4
rules3
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/Requests/index.html b/tests/coverage/app/Http/Requests/index.html new file mode 100644 index 0000000..3b4e11f --- /dev/null +++ b/tests/coverage/app/Http/Requests/index.html @@ -0,0 +1,212 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Requests + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
Api
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
CustomerExport.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
GenerateServiceToken.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ManageBrandRequest.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ManageServiceRequest.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
QuestionExport.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StatsByBrandRequest.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StatsByQuestionRequest.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Http/Resources/Api/V2/AnswersResource.php.html b/tests/coverage/app/Http/Resources/Api/V2/AnswersResource.php.html new file mode 100644 index 0000000..22c6aec --- /dev/null +++ b/tests/coverage/app/Http/Resources/Api/V2/AnswersResource.php.html @@ -0,0 +1,182 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Resources/Api/V2/AnswersResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
AnswersResource
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Models\Answer;
6use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
7
8class AnswersResource extends JsonResourceCollection
9{
10    /**
11     * Transform the resource into an array.
12     *
13     * @param  mixed  $request
14     * @return array
15     */
16    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
17    public function toArray($request)
18    {
19        return $this->collection->map(function (Answer $answer) {
20            return [
21                'id' => $answer->id,
22                'answer' => $answer->answer
23            ];
24        });
25    }
26}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Resources/Api/V2/ApiResponse.php.html b/tests/coverage/app/Http/Resources/Api/V2/ApiResponse.php.html new file mode 100644 index 0000000..0cebd7d --- /dev/null +++ b/tests/coverage/app/Http/Resources/Api/V2/ApiResponse.php.html @@ -0,0 +1,219 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Resources/Api/V2/ApiResponse.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
ApiResponse
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Enums\ApiStatus;
6use Illuminate\Http\Resources\Json\JsonResource;
7use stdClass;
8
9class ApiResponse extends JsonResource
10{
11    /** @var stdClass */
12    protected $data;
13
14    /** @var \App\Enums\ApiStatus */
15    protected $status;
16
17    /**
18     * @param stdClass  $data
19     * @param ApiStatus $status
20     */
21    public function __construct(stdClass $data = null, ApiStatus $status)
22    {
23        $this->status = $status;
24        $this->data   = $data;
25    }
26
27    /**
28     * Transform the resource into an array.
29     *
30     * @param  mixed  $request
31     * @return array
32     */
33    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
34    public function toArray($request): array
35    {
36        return [
37            'status' => $this->status->value,
38            'data' => (array)$this->data,
39            'version' => config('api.version')
40        ];
41    }
42}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Resources/Api/V2/BrandStatsResource.php.html b/tests/coverage/app/Http/Resources/Api/V2/BrandStatsResource.php.html new file mode 100644 index 0000000..d56a780 --- /dev/null +++ b/tests/coverage/app/Http/Resources/Api/V2/BrandStatsResource.php.html @@ -0,0 +1,189 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Resources/Api/V2/BrandStatsResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
BrandStatsResource
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Models\BrandStat;
6use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
7
8class BrandStatsResource extends JsonResourceCollection
9{
10    /**
11     * Transform the resource into an array.
12     *
13     * @param  mixed  $request
14     * @return array
15     */
16    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
17    public function toArray($request)
18    {
19        return [
20            'stats' => $this->collection->map(function (BrandStat $brandStat) {
21                return [
22                    'date' => $brandStat->created_at->format('Y-m-d'),
23                    'active' => $brandStat->active,
24                    'inactive' => $brandStat->inactive,
25                    'old_accounts' => $brandStat->old_accounts,
26                    'new_accounts' => $brandStat->new_accounts,
27                    'total_accounts' => $brandStat->total_accounts,
28                    'total_opt_in' => $brandStat->total_opt_in
29                ];
30            })
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Resources/Api/V2/CustomerResource.php.html b/tests/coverage/app/Http/Resources/Api/V2/CustomerResource.php.html new file mode 100644 index 0000000..58b21c1 --- /dev/null +++ b/tests/coverage/app/Http/Resources/Api/V2/CustomerResource.php.html @@ -0,0 +1,298 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Resources/Api/V2/CustomerResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
CustomerResource
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
 getSkeleton
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Models\Brand;
6use App\Models\Customer;
7use Illuminate\Http\Resources\Json\JsonResource;
8
9class CustomerResource extends JsonResource
10{
11    /** @var \App\Models\Customer */
12    private $customer;
13
14    /** @var \App\Models\CustomerInfo */
15    private $info;
16
17    /** @var \App\Models\CustomerLogin */
18    private $login;
19
20    /** @var array */
21    private static $skeleton = [
22        'hashcode' => '',
23        'firstname' => '',
24        'lastname' => '',
25        'email' => '',
26        'birth_date' => '',
27        'address' => '',
28        'address2' => '',
29        'phone' => '',
30        'country' => '',
31        'city' => '',
32        'postcode' => '',
33        'opt_in' => '',
34        'status' => '',
35        'last_connection' => ''
36    ];
37
38    /**
39     * CustomerResource
40     *
41     * @param Brand    $brand
42     * @param Customer $customer
43     */
44    public function __construct(Brand $brand, Customer $customer)
45    {
46        $this->customer = $customer;
47        $this->info     = $customer->info($brand, Customer::FIRST_OR_FAIL);
48        $this->login    = $customer->login($brand, Customer::FIRST_OR_FAIL);
49    }
50
51    /**
52     * Transform the resource into an array.
53     *
54     * @param \Illuminate\Http\Request|null $request
55     * @return array
56     * + email string
57     * + firstname string
58     * + lastname string
59     * + birth_date datetime
60     * + address string
61     * + address2 string
62     * + phone string
63     * + country string
64     * + city string
65     * + postcode string
66     * + opt_in boolean
67     * + hashcode string
68     * + status boolean
69     * + last_connection date
70     */
71    //phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
72    public function toArray($request = null)
73    {
74        $data = array_intersect_key(
75            array_merge(
76                [
77                    'email' => $this->customer->email
78                ],
79                $this->login->toArray(),
80                $this->info->toArray(),
81                ['status' => $this->login->status]
82            ),
83            $this->getSkeleton()
84        );
85
86        $data['country']  = $this->info->country->value;
87        $data['address2'] = $this->info->address2 ?? '';
88        return $data;
89    }
90
91    /**
92     * Returns the resource skeletton
93     *
94     * @return array
95     */
96    public static function getSkeleton(): array
97    {
98        return self::$skeleton;
99    }
100}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Resources/Api/V2/QuestionStatsResource.php.html b/tests/coverage/app/Http/Resources/Api/V2/QuestionStatsResource.php.html new file mode 100644 index 0000000..fb7e590 --- /dev/null +++ b/tests/coverage/app/Http/Resources/Api/V2/QuestionStatsResource.php.html @@ -0,0 +1,218 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Resources/Api/V2/QuestionStatsResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
QuestionStatsResource
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Models\Question;
6use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
7use Illuminate\Support\Collection;
8
9class QuestionStatsResource extends JsonResourceCollection
10{
11    /** @var \App\Models\Question */
12    protected $question = null;
13
14    /**
15     * Create a new resource instance.
16     *
17     * @param  \Illuminate\Support\Collection $questionStats
18     * @param  \App\Models\Question           $question
19     * @return void
20     */
21    public function __construct(Collection $questionStats = null, Question $question)
22    {
23        $this->question = $question;
24        parent::__construct($questionStats);
25    }
26
27    /**
28     * Transform the resource into an array.
29     *
30     * @param  mixed $request
31     * @return array
32     */
33    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
34    public function toArray($request)
35    {
36        return [
37            'answers' => (new AnswersResource($this->question->answers))->toArray($request),
38            'stats' => $this->collection->toArray()
39        ];
40    }
41}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Resources/Api/V2/QuestionsResource.php.html b/tests/coverage/app/Http/Resources/Api/V2/QuestionsResource.php.html new file mode 100644 index 0000000..8125c99 --- /dev/null +++ b/tests/coverage/app/Http/Resources/Api/V2/QuestionsResource.php.html @@ -0,0 +1,231 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Resources/Api/V2/QuestionsResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
QuestionsResource
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
3
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 toArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources\Api\V2;
4
5use App\Models\Answer;
6use App\Models\Question;
7use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
8use Illuminate\Support\Collection;
9
10class QuestionsResource extends JsonResourceCollection
11{
12    /** @var \Illuminate\Support\Collection */
13    protected $userAnwsers = null;
14
15    /**
16     * Create a new resource instance.
17     *
18     * @param  mixed                          $resource
19     * @param  \Illuminate\Support\Collection $userAnwsers
20     * @return void
21     */
22    public function __construct($resource, Collection $userAnwsers = null)
23    {
24        $this->userAnwsers = $userAnwsers;
25        parent::__construct($resource);
26    }
27
28    /**
29     * Transform the resource into an array.
30     *
31     * @param  mixed  $request
32     * @return array
33     */
34    // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
35    public function toArray($request)
36    {
37        return $this->collection->map(function (Question $question) {
38            /** @var \Illuminate\Support\Collection */
39            $answers  = $question->answers;
40            $question = [
41                'id' => $question->id,
42                'type' => $question->type->value,
43                'question' => $question->question,
44                'answers' => new AnswersResource($answers)
45            ];
46            if ($this->userAnwsers) {
47                $question['customerAnswers'] = new AnswersResource($answers->filter(function (Answer $answer) {
48                    return $this->userAnwsers->keyBy('id')->has($answer->id);
49                }));
50            }
51            return $question;
52        });
53    }
54}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Resources/Api/V2/dashboard.html b/tests/coverage/app/Http/Resources/Api/V2/dashboard.html new file mode 100644 index 0000000..b9fe381 --- /dev/null +++ b/tests/coverage/app/Http/Resources/Api/V2/dashboard.html @@ -0,0 +1,288 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http/Resources/Api/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/Resources/Api/V2/index.html b/tests/coverage/app/Http/Resources/Api/V2/index.html new file mode 100644 index 0000000..6b35ea1 --- /dev/null +++ b/tests/coverage/app/Http/Resources/Api/V2/index.html @@ -0,0 +1,263 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Resources/Api/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
AnswersResource.php
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiResponse.php
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
BrandStatsResource.php
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CustomerResource.php
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
QuestionStatsResource.php
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
QuestionsResource.php
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/app/Http/Resources/Api/dashboard.html b/tests/coverage/app/Http/Resources/Api/dashboard.html new file mode 100644 index 0000000..af3971e --- /dev/null +++ b/tests/coverage/app/Http/Resources/Api/dashboard.html @@ -0,0 +1,287 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http/Resources/Api + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/Resources/Api/index.html b/tests/coverage/app/Http/Resources/Api/index.html new file mode 100644 index 0000000..cab6a93 --- /dev/null +++ b/tests/coverage/app/Http/Resources/Api/index.html @@ -0,0 +1,122 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Resources/Api + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
V2
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ +
+ + diff --git a/tests/coverage/app/Http/Resources/CustomerResource.php.html b/tests/coverage/app/Http/Resources/CustomerResource.php.html new file mode 100644 index 0000000..149b412 --- /dev/null +++ b/tests/coverage/app/Http/Resources/CustomerResource.php.html @@ -0,0 +1,181 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Resources/CustomerResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerResource
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 toArray
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources;
4
5use Illuminate\Http\Resources\Json\JsonResource;
6
7/**
8 * @codeCoverageIgnore
9 */
10class CustomerResource extends JsonResource
11{
12    /** @var integer */
13    private $brandId;
14
15    /**
16     * @param mixed $resource
17     * @param mixed $brandId
18     */
19    public function __construct($resource, $brandId)
20    {
21        parent::__construct($resource);
22        $this->brandId = $brandId;
23    }
24
25    /**
26     * Transform the resource into an array.
27     *
28     * @param  mixed  $request
29     * @return array
30     */
31    //phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
32    public function toArray($request)
33    {
34        $infos = $this->infos()->where('brand_id', $this->brandId)->first();
35        $login = $this->logins()->where('brand_id', $this->brandId)->first();
36        return [
37            'status' => 'OK',
38            'user' => [
39                'crm_id' => $this->id,
40                'firstname' => $infos->firstname,
41                'lastname' => $infos->lastname,
42                'email' => $this->email,
43                'birth_date' => $infos->birth_date,
44                'address' => $infos->address,
45                'address2' => $infos->address2,
46                'phone' => $infos->phone,
47                'country' => $infos->country->label,
48                'city' => $infos->city,
49                'postcode' => $infos->postcode,
50                'opt_in' => $infos->opt_in,
51                'hashcode' => $infos->hashcode,
52                'status' => $login->status,
53            ]
54        ];
55    }
56}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Resources/StatusResource.php.html b/tests/coverage/app/Http/Resources/StatusResource.php.html new file mode 100644 index 0000000..eb18cde --- /dev/null +++ b/tests/coverage/app/Http/Resources/StatusResource.php.html @@ -0,0 +1,115 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Resources/StatusResource.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StatusResource
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Http\Resources;
4
5use Illuminate\Http\Resources\Json\JsonResource;
6
7/**
8 * @codeCoverageIgnore
9 */
10class StatusResource extends JsonResource
11{
12}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Http/Resources/dashboard.html b/tests/coverage/app/Http/Resources/dashboard.html new file mode 100644 index 0000000..85f2acd --- /dev/null +++ b/tests/coverage/app/Http/Resources/dashboard.html @@ -0,0 +1,286 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http/Resources + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/Resources/index.html b/tests/coverage/app/Http/Resources/index.html new file mode 100644 index 0000000..9ebd078 --- /dev/null +++ b/tests/coverage/app/Http/Resources/index.html @@ -0,0 +1,147 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http/Resources + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
Api
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
CustomerResource.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StatusResource.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Http/dashboard.html b/tests/coverage/app/Http/dashboard.html new file mode 100644 index 0000000..4c0bc7b --- /dev/null +++ b/tests/coverage/app/Http/dashboard.html @@ -0,0 +1,293 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Http + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+ +
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
rules77%
rules89%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + +
MethodCRAP
rules4
rules3
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Http/index.html b/tests/coverage/app/Http/index.html new file mode 100644 index 0000000..af2f177 --- /dev/null +++ b/tests/coverage/app/Http/index.html @@ -0,0 +1,217 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Http + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 99.10% covered (success) +
+
+
99.10%
442 / 446
+
+ 96.15% covered (success) +
+
+
96.15%
50 / 52
+
+ 92.86% covered (success) +
+
+
92.86%
26 / 28
Controllers
+
+ 100.00% covered (success) +
+
+
100.00%
177 / 177
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
Middleware
+
+ 100.00% covered (success) +
+
+
100.00%
37 / 37
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
Requests
+
+ 97.75% covered (success) +
+
+
97.75%
174 / 178
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
Resources
+
+ 100.00% covered (success) +
+
+
100.00%
54 / 54
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
Kernel.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Jobs/SetServiceStats.php.html b/tests/coverage/app/Jobs/SetServiceStats.php.html new file mode 100644 index 0000000..4c5a6a3 --- /dev/null +++ b/tests/coverage/app/Jobs/SetServiceStats.php.html @@ -0,0 +1,228 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Jobs/SetServiceStats.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
SetServiceStats
n/a
0 / 0
n/a
0 / 0
5
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 handle
n/a
0 / 0
2
n/a
0 / 0
 retryUntil
n/a
0 / 0
1
n/a
0 / 0
 middleware
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Jobs;
4
5use App\Models\Service;
6use App\Models\ServiceStat;
7use Illuminate\Bus\Queueable;
8use Illuminate\Contracts\Queue\ShouldQueue;
9use Illuminate\Foundation\Bus\Dispatchable;
10use Illuminate\Queue\InteractsWithQueue;
11use Illuminate\Queue\Middleware\WithoutOverlapping;
12use Illuminate\Queue\SerializesModels;
13
14/**
15 * @codeCoverageIgnore
16 */
17class SetServiceStats implements ShouldQueue
18{
19    use Dispatchable;
20    use InteractsWithQueue;
21    use Queueable;
22    use SerializesModels;
23
24    private const KEY = '2202020';
25
26    /** @var \App\Models\Service */
27    protected $service;
28
29    /**
30     * Create a new job instance.
31     *
32     * @param Service $service
33     * @return void
34     */
35    public function __construct(Service $service)
36    {
37        $this->service = $service;
38    }
39
40    /**
41     * Execute the job.
42     *
43     * @return void
44     */
45    public function handle()
46    {
47        $serviceStat = ServiceStat::firstOrNew([
48            'date' => date('Y-m-d'),
49            'service_id' => $this->service->id,
50            'brand_id' => $this->service->brand->id,
51        ]);
52        if (!$serviceStat->exists) {
53            $serviceStat->service()->associate($this->service);
54            $serviceStat->brand()->associate($this->service->brand);
55        }
56        $serviceStat->nb_calls += 1;
57        $serviceStat->saveOrFail();
58    }
59
60    /**
61     * Determine the time at which the job should timeout.
62     *
63     * @return \DateTime
64     */
65    public function retryUntil()
66    {
67        return now()->addMinutes(rand(0, 360));
68    }
69
70    /**
71     * Get the middleware the job should pass through.
72     *
73     * @return array
74     */
75    public function middleware()
76    {
77        return [
78            (new WithoutOverlapping(self::KEY))
79                ->releaseAfter(rand(90, 180))
80        ];
81    }
82}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Jobs/StartStatJobs.php.html b/tests/coverage/app/Jobs/StartStatJobs.php.html new file mode 100644 index 0000000..6f9c924 --- /dev/null +++ b/tests/coverage/app/Jobs/StartStatJobs.php.html @@ -0,0 +1,146 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Jobs/StartStatJobs.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StartStatJobs
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 handle
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Jobs;
4
5use App\Models\Brand;
6use Illuminate\Bus\Queueable;
7use Illuminate\Contracts\Queue\ShouldQueue;
8use Illuminate\Foundation\Bus\Dispatchable;
9use Illuminate\Queue\InteractsWithQueue;
10
11/**
12 * @codeCoverageIgnore
13 */
14class StartStatJobs implements ShouldQueue
15{
16    use Dispatchable;
17    use InteractsWithQueue;
18    use Queueable;
19
20    /**
21     * Execute the job.
22     *
23     * @return void
24     */
25    public function handle()
26    {
27        $brands = Brand::all();
28        foreach ($brands as $brand) {
29            \dispatch(new StatJob($brand))
30                ->delay(rand(0, 90));
31        }
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Jobs/StatJob.php.html b/tests/coverage/app/Jobs/StatJob.php.html new file mode 100644 index 0000000..e905a30 --- /dev/null +++ b/tests/coverage/app/Jobs/StatJob.php.html @@ -0,0 +1,221 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Jobs/StatJob.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
StatJob
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 handle
n/a
0 / 0
1
n/a
0 / 0
 middleware
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Jobs;
4
5use App\Models\Brand;
6use App\Models\BrandStat;
7use App\Models\CustomerInfo;
8use App\Models\CustomerLogin;
9use App\Models\SingleOptin;
10use Illuminate\Bus\Queueable;
11use Illuminate\Contracts\Queue\ShouldQueue;
12use Illuminate\Foundation\Bus\Dispatchable;
13use Illuminate\Queue\InteractsWithQueue;
14use Illuminate\Queue\Middleware\WithoutOverlapping;
15use Illuminate\Queue\SerializesModels;
16use Illuminate\Support\Carbon;
17
18/**
19 * @codeCoverageIgnore
20 */
21class StatJob implements ShouldQueue
22{
23    use Dispatchable;
24    use InteractsWithQueue;
25    use Queueable;
26    use SerializesModels;
27
28    /**
29     * Set high max tries as using prevent overlap
30     *
31     * @var integer
32     */
33    public $tries = 10;
34
35    /** Used to prevent overlaping */
36    private const JOBKEY = 32644296;
37
38    /** @var \App\Models\Brand */
39    private $brand;
40
41    /**
42     * Create a new job instance.
43     *
44     * @param Brand $brand
45     * @return void
46     */
47    public function __construct(Brand $brand)
48    {
49        $this->brand = $brand;
50    }
51
52    /**
53     * Execute the job.
54     *
55     * @return void
56     */
57    public function handle()
58    {
59        $stats = new BrandStat([
60            'active' => CustomerLogin::whereBrandId($this->brand->id)
61                ->whereDate('last_connection', '<', Carbon::now()->subMonths(6))->count(),
62            'inactive' => CustomerLogin::whereBrandId($this->brand->id)
63                ->whereDate('last_connection', '>', Carbon::now()->subMonths(6))->count(),
64            'new_accounts' => CustomerLogin::whereBrandId($this->brand->id)
65                ->whereDate('created_at', '<', Carbon::now()->subMonths(6))->count(),
66            'old_accounts' => CustomerLogin::whereBrandId($this->brand->id)
67                ->whereDate('created_at', '>', Carbon::now()->subMonths(6))->count(),
68            'total_accounts' => CustomerLogin::whereBrandId($this->brand->id)->count(),
69            'total_opt_in' => CustomerInfo::whereBrandId($this->brand->id)
70                ->whereOptIn(true)->count() + SingleOptin::whereBrandId($this->brand->id)->count()
71        ]);
72        $stats->brand()->associate($this->brand);
73        $stats->saveOrFail();
74    }
75
76    /**
77     * Get the middleware the job should pass through.
78     *
79     * @return array
80     */
81    public function middleware()
82    {
83        // * Dispatch randomly overlaping jobs if overlapping.
84        return [(new WithoutOverlapping(self::JOBKEY))->releaseAfter(rand(20, 90))];
85    }
86}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Jobs/UpdateCustomerLastConnection.php.html b/tests/coverage/app/Jobs/UpdateCustomerLastConnection.php.html new file mode 100644 index 0000000..19d8c0f --- /dev/null +++ b/tests/coverage/app/Jobs/UpdateCustomerLastConnection.php.html @@ -0,0 +1,187 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Jobs/UpdateCustomerLastConnection.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
UpdateCustomerLastConnection
n/a
0 / 0
n/a
0 / 0
3
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 handle
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Jobs;
4
5use App\Models\Customer;
6use App\Models\CustomerLogin;
7use Illuminate\Bus\Queueable;
8use Illuminate\Contracts\Queue\ShouldQueue;
9use Illuminate\Foundation\Bus\Dispatchable;
10use Illuminate\Queue\InteractsWithQueue;
11use Illuminate\Queue\SerializesModels;
12use Illuminate\Support\Carbon;
13
14/**
15 * @codeCoverageIgnore
16 */
17class UpdateCustomerLastConnection implements ShouldQueue
18{
19    use Dispatchable;
20    use InteractsWithQueue;
21    use Queueable;
22    use SerializesModels;
23
24    /** @var \Illuminate\Support\Carbon */
25    protected $date = null;
26
27    /** @var \App\Models\Customer */
28    protected $customer = null;
29
30    /** @var \App\Models\CustomerLogin */
31    protected $customerLogin = null;
32
33    /**
34     * Create a new job instance.
35     *
36     * @param Customer      $customer
37     * @param CustomerLogin $customerLogin
38     * @param Carbon        $date
39     * @return void
40     */
41    public function __construct(
42        Customer $customer,
43        CustomerLogin $customerLogin,
44        Carbon $date
45    ) {
46        $this->date          = $date;
47        $this->customer      = $customer;
48        $this->customerLogin = $customerLogin;
49    }
50
51    /**
52     * Execute the job.
53     *
54     * @return void
55     */
56    public function handle()
57    {
58        if ($this->customerLogin->last_connection < $this->date) {
59            $this->customerLogin->last_connection = $this->date;
60            $this->customer->logins()->save($this->customerLogin);
61        }
62    }
63}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Jobs/dashboard.html b/tests/coverage/app/Jobs/dashboard.html new file mode 100644 index 0000000..d527daf --- /dev/null +++ b/tests/coverage/app/Jobs/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Jobs + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Jobs/index.html b/tests/coverage/app/Jobs/index.html new file mode 100644 index 0000000..a8a1db5 --- /dev/null +++ b/tests/coverage/app/Jobs/index.html @@ -0,0 +1,129 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Jobs + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
SetServiceStats.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StartStatJobs.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
StatJob.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
UpdateCustomerLastConnection.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Lib/Helper.php.html b/tests/coverage/app/Lib/Helper.php.html new file mode 100644 index 0000000..a1bf6e6 --- /dev/null +++ b/tests/coverage/app/Lib/Helper.php.html @@ -0,0 +1,494 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Lib/Helper.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
CRAP
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
Helper
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
28.61
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
 isRequestForApiV2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 isRequestForApiV1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 getRouteName
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 getBearerTokenFromRequest
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
20
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 7
 getRoutePrefix
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 getRoute
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 getPackageInfo
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 signRequest
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
7.10
+
+ 87.50% covered (warning) +
+
+
87.50%
35 / 40
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Lib;
4
5use App\Exceptions\Api\V2\CrmException;
6use Illuminate\Contracts\Encryption\DecryptException;
7use Illuminate\Http\Request;
8use Illuminate\Support\Facades\Crypt;
9use Illuminate\Support\Facades\Route;
10use Illuminate\Support\Str;
11
12/**
13 * Helper Class
14 */
15class Helper
16{
17    /**
18     * Package infos
19     *
20     * @var array
21     */
22    private static $packageInfos;
23
24    /**
25     * Tells if the request is within apiV2 routes
26     *
27     * @param Request $request
28     * @return boolean
29     */
30    public static function isRequestForApiV2(Request $request)
31    {
32        // * Small hack to ensure to know if route is From APIv2.
33        $routeName = static::getRouteName($request) ??
34            (strpos($request->path(), 'api/v2') === 0 ? 'apiV2.' : null) ?? 'none';
35        return strpos($routeName, 'apiV2.') === 0;
36    }
37
38    /**
39     * Tells if the request is within apiV1 routes
40     *
41     * @param Request $request
42     * @return boolean
43     */
44    public static function isRequestForApiV1(Request $request): bool
45    {
46        // * Small hack to ensure to know if route is From APIv1.
47        $routePrefix = (strpos(static::getRoutePrefix($request), 'api/v1') ? 'v1' : null) ??
48            (strpos($request->path(), 'api/v1') === 0 ? 'v1' : null) ?? 'none';
49        return strpos($routePrefix, 'v1') === 0;
50    }
51
52    /**
53     * Try to get current route name
54     *
55     * @param Request $request
56     * @return string|null
57     */
58    public static function getRouteName(Request $request)
59    {
60        return Route::currentRouteName() ?? ($request->route() ? $request->route()->getName() : null);
61    }
62
63    /**
64     * Get the bearer token from request
65     *
66     * @param Request $request
67     * @return string|null
68     */
69    public static function getBearerTokenFromRequest(Request $request): ?string
70    {
71        $header = $request->header('Authorization', '');
72
73        if (Str::contains($header, 'Bearer ')) {
74            $auths = \explode(',', $header);
75            foreach ($auths as $auth) {
76                if (Str::contains($auth, 'Bearer ')) {
77                    return Str::substr($auth, 7);
78                }
79            }
80        }
81        return null;
82    }
83
84    /**
85     * Try to get current route prefix
86     *
87     * @param Request $request
88     * @return string|null
89     */
90    public static function getRoutePrefix(Request $request)
91    {
92        return (Route::getCurrentRoute() ? Route::getCurrentRoute()->getPrefix() : null) ??
93            ($request->route() ? $request->route()->getPrefix() : null);
94    }
95
96    /**
97     * Try to get route from request
98     *
99     * @param Request $request
100     * @return \Illuminate\Routing\Route|null
101     */
102    public static function getRoute(Request $request)
103    {
104        return Route::getRoutes()->getByName(static::getRouteName($request));
105    }
106
107    /**
108     * Get composer.json infos
109     *
110     * @return array
111     */
112    public static function getPackageInfo(): array
113    {
114        if (!static::$packageInfos) {
115            static::$packageInfos = \json_decode(\file_get_contents(__DIR__ . '/../../composer.json'), true);
116        }
117        return static::$packageInfos;
118    }
119
120    /**
121     * Calculate the request signature
122     *
123     * @param string $secret
124     * @param string $uri
125     * @param string $method
126     * @param array  $parameters
127     * @return string request signature
128     * @throws CrmException If an error occurs.
129     */
130    public static function signRequest(string $secret, string $uri, string $method, array $parameters): string
131    {
132        try {
133            $uri    = collect(\parse_url($uri));
134            $method = \strtolower($method);
135
136            // * Remove signature from parameters to calculate the hashmac.
137            $signature = collect([
138                'uri' => sprintf(
139                    '%s://%s%s',
140                    $uri->get('scheme'),
141                    $uri->get('host'),
142                    $uri->get('path')
143                ),
144                'method' => $method
145            ]);
146
147            $flattenRequestArray = function (array $array) use (&$flattenRequestArray) {
148                $out = [];
149                foreach ($array as $key => $value) {
150                    if (is_array($value)) {
151                        $subArray = $flattenRequestArray($value);
152                        foreach ($subArray as $subKey => $subValue) {
153                            $out["$key.$subKey"] = $subValue;
154                        }
155                        continue;
156                    }
157                    $out[$key] = $value;
158                }
159                return $out;
160            };
161
162            $parameters = $flattenRequestArray($parameters);
163            $parameters = collect($parameters);
164
165            // * Remove signature from parameters.
166            $parameters->pull('signature');
167
168            // * Natural sort all parameters.
169            $parameters = $parameters->sortKeys(\SORT_NATURAL)
170                ->map(function ($value, $key) {
171                    return sprintf('%s=%s', \rawurlencode($key), \rawurlencode($value));
172                });
173
174            $signature->push($parameters->implode('&'));
175
176            $calculatedHash = base64_encode(hash_hmac(
177                'sha256',
178                $signature->implode('\n'),
179                Crypt::decryptString($secret),
180                true
181            ));
182
183            if (!$calculatedHash) {
184                throw new CrmException('Failed to calculate requests hash');
185            }
186
187            return $calculatedHash;
188        } catch (DecryptException $e) {
189            throw new CrmException('Failed to decrypt service credentials', 0, $e);
190        } catch (\Exception $e) {
191            throw new CrmException('An error occured when authenticating service', 0, $e);
192        }//end try
193    }
194}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Lib/MigrateOldData.php.html b/tests/coverage/app/Lib/MigrateOldData.php.html new file mode 100644 index 0000000..84fc8c6 --- /dev/null +++ b/tests/coverage/app/Lib/MigrateOldData.php.html @@ -0,0 +1,390 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Lib/MigrateOldData.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
MigrateOldData
n/a
0 / 0
n/a
0 / 0
19
n/a
0 / 0
 runMigration
n/a
0 / 0
1
n/a
0 / 0
 instruct
n/a
0 / 0
15
n/a
0 / 0
 initFile
n/a
0 / 0
1
n/a
0 / 0
 write
n/a
0 / 0
2
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Lib;
4
5use App\Exceptions\AbortRowImportException;
6use App\Models\Brand;
7use App\Models\Customer;
8use App\Models\CustomerInfo;
9use App\Models\CustomerLogin;
10use App\Models\SingleOptin;
11use Carbon\Exceptions\InvalidFormatException;
12use Database\migrations\LeGaulois2016Migration;
13use Illuminate\Support\Carbon;
14use Illuminate\Support\Facades\File;
15use Illuminate\Support\Facades\Hash;
16use Illuminate\Support\Facades\Validator;
17use Illuminate\Support\Str;
18
19/**
20 * @codeCoverageIgnore
21 */
22class MigrateOldData extends LeGaulois2016Migration
23{
24    /** @var Brand */
25    private $brand;
26
27    /**
28     * runMigration
29     *
30     * @param Brand  $brand
31     * @param string $table
32     * @return void
33     */
34    public function runMigration(Brand $brand, string $table)
35    {
36        $this->table = $table;
37        $this->brand = $brand;
38        $this->instruct();
39        $this->up();
40    }
41
42    /**
43     * instruct
44     *
45     * @return array
46     */
47    // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Squiz.Commenting.FunctionComment.Missing
48    public function instruct(): array
49    {
50        $this->initFile();
51        $cleanDegeu = function (string $str) {
52            return html_entity_decode(
53                trim(str_replace('\\', '', ucwords(stripcslashes(strtolower($str))))),
54                ENT_QUOTES | ENT_HTML5,
55                'UTF-8'
56            );
57        };
58        return [
59            'Users' => [
60                'table' => $this->table,
61                'model' => Customer::class,
62                'comment' => sprintf('Migrating %s customers', $this->brand->name),
63                'bind' => [
64                    'email' => [
65                        'apply' => function ($v, $model) use ($cleanDegeu) {
66                            $v                      = \mb_convert_encoding($v, "UTF-8");
67                            $v                      = explode('#|#', $v);
68                            $oldId                  = $v[0];
69                            $first_name             = $cleanDegeu($v[1]);
70                            $last_name              = $cleanDegeu($v[2]);
71                            $birth_date             = $v[3];
72                            $address                = $cleanDegeu($v[4]);
73                            $address2               = $cleanDegeu($v[5]);
74                            $city                   = $cleanDegeu($v[6]);
75                            $postcode               = $v[7];
76                            $user_inscritNewsletter = $v[8];
77                            $email                  = strtolower($v[9]);
78                            $pwd                    = $v[10];
79                            $created_at             = $v[11];
80                            $updated_at             = $v[12];
81                            $last_cnx               = $v[13];
82
83                            // * Just save Single optin.
84                            if (empty($first_name) && empty($last_name)) {
85                                $sglOptin           = SingleOptin::where('email', strtolower($email))
86                                    ->where('brand_id', $this->brand->id)->firstOrNew();
87                                $sglOptin->email    = strtolower($email);
88                                $sglOptin->brand_id = $this->brand->id;
89                                $sglOptin->save();
90                            } else {
91                                // * Validate Data.
92                                $d = [
93                                    'first_name' => $first_name,
94                                    'last_name' => $last_name,
95                                    'address' => $address,
96                                    'address2' => $address2,
97                                    'city' => $city,
98                                    'postcode' => $postcode,
99                                ];
100
101                                $validator = Validator::make($d, [
102                                    'first_name' => 'min:2',
103                                    'last_name' => 'min:2'
104                                ]);
105
106                                if ($validator->fails()) {
107                                    throw new AbortRowImportException();
108                                }
109
110                                $rules = [
111                                    'address' => 'string|min:2',
112                                    'address2' => 'string|min:2',
113                                    'city' => 'string|min:2',
114                                    'postcode' => 'digits_between:4,5'
115                                ];
116
117                                $validate = function (array $data, array $rule) {
118                                    return Validator::make($data, $rule)->fails();
119                                };
120
121                                foreach ($rules as $key => $rule) {
122                                    if ($validate([$key => $d[$key]], [$key => $rule])) {
123                                        ${$key} = null;
124                                    }
125                                }
126
127                                $model             = Customer::where('email', $email)->firstOrNew();
128                                $model->email      = $email;
129                                $model->created_at = $created_at;
130                                $model->updated_at = $updated_at;
131                                $model->save();
132
133                                // * Filtre birthdate puisque c'est du sale dans la vielle crm.
134                                try {
135                                    $expl = explode('-', $birth_date);
136                                    $d    = Carbon::parse($birth_date);
137                                    if (
138                                        $d->toDateString() === sprintf(
139                                            '%s-%s-%s',
140                                            $expl[2] ?? '',
141                                            $expl[1] ?? '',
142                                            $expl[0] ?? ''
143                                        )
144                                    ) {
145                                        $birth_date = $d->toDateString();
146                                    }
147                                } catch (InvalidFormatException $e) {
148                                    $birth_date = '';
149                                }
150
151
152                                if ($birth_date === '0000-00-00') {
153                                    $birth_date = '';
154                                }
155
156                                $city     = !empty($city) ? $city : null;
157                                $postcode = !empty($postcode) ? $postcode : null;
158                                $address  = !empty($address) ? $address : null;
159                                $address2 = !empty($address2) ? $address2 : null;
160
161                                $cInfo              = new CustomerInfo();
162                                $cInfo->hashcode    = Str::uuid();
163                                $cInfo->firstname   = ucfirst(strtolower($first_name));
164                                $cInfo->lastname    = ucfirst(strtolower($last_name));
165                                $cInfo->birth_date  = !strlen($birth_date) ? null : $birth_date;
166                                $cInfo->address     = $address;
167                                $cInfo->address2    = $address2;
168                                $cInfo->city        = $city;
169                                $cInfo->postcode    = $postcode;
170                                $cInfo->opt_in      = $user_inscritNewsletter ? true : false;
171                                $cInfo->brand_id    = $this->brand->id;
172                                $cInfo->customer_id = $model->id;
173                                $cInfo->created_at  = $created_at;
174                                $cInfo->updated_at  = $updated_at;
175                                $cInfo->save();
176
177                                $cLogin                  = new CustomerLogin();
178                                $cLogin->password        = Hash::make(trim($pwd));
179                                $cLogin->brand_id        = $this->brand->id;
180                                $cLogin->customer_id     = $model->id;
181                                $cLogin->status          = true;
182                                $cLogin->created_at      = $created_at;
183                                $cLogin->updated_at      = $updated_at;
184                                $cLogin->last_connection = $last_cnx;
185                                $cLogin->save();
186
187                                $this->write(
188                                    [
189                                        'new' => $model->id,
190                                        'old' => $oldId,
191                                        'hashcode' => $cInfo->hashcode,
192                                        'created_at' => $created_at,
193                                        'updated_at' => $updated_at
194                                    ]
195                                );
196                            }//end if
197                            throw new AbortRowImportException();
198                        },
199                        'name' => [
200                            'user_id',
201                            'firstname',
202                            'lastname',
203                            'birthdate',
204                            'address',
205                            'address2',
206                            'city',
207                            'postcode',
208                            'optin',
209                            'email',
210                            'pwd',
211                            'created_at',
212                            'updated_at',
213                            'last_cnx'
214                        ]
215                    ],
216                ]
217            ],
218        ];
219    }
220
221    /**
222     * Create the migration file
223     *
224     * @return void
225     */
226    public function initFile()
227    {
228        File::put(storage_path('crm_migration.csv'), sprintf('new;old;hashcode;created_at;updated_at') . PHP_EOL);
229    }
230
231    /**
232     * Write date in the migration file
233     *
234     * @param array $data
235     * @return void
236     */
237    public function write(array $data)
238    {
239        foreach ($data as &$d) {
240            $d = sprintf('"%s"', $d);
241        }
242        File::append(storage_path('crm_migration.csv'), implode(';', $data) . PHP_EOL);
243    }
244}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Lib/dashboard.html b/tests/coverage/app/Lib/dashboard.html new file mode 100644 index 0000000..753dbcb --- /dev/null +++ b/tests/coverage/app/Lib/dashboard.html @@ -0,0 +1,291 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Lib + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + +
ClassCoverage
App\Lib\Helper80%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + +
ClassCRAP
App\Lib\Helper28
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
getBearerTokenFromRequest0%
signRequest87%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + +
MethodCRAP
getBearerTokenFromRequest20
signRequest7
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Lib/index.html b/tests/coverage/app/Lib/index.html new file mode 100644 index 0000000..bb2ab5a --- /dev/null +++ b/tests/coverage/app/Lib/index.html @@ -0,0 +1,133 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Lib + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
Helper.php
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
MigrateOldData.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Mail/CourrierArcadiaError.php.html b/tests/coverage/app/Mail/CourrierArcadiaError.php.html new file mode 100644 index 0000000..f7e7506 --- /dev/null +++ b/tests/coverage/app/Mail/CourrierArcadiaError.php.html @@ -0,0 +1,188 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Mail/CourrierArcadiaError.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CourrierArcadiaError
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 build
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Mail;
4
5use Illuminate\Bus\Queueable;
6use Illuminate\Contracts\Queue\ShouldQueue;
7use Illuminate\Mail\Mailable;
8use Illuminate\Queue\SerializesModels;
9
10/**
11 * @codeCoverageIgnore
12 */
13class CourrierArcadiaError extends Mailable implements ShouldQueue
14{
15    use Queueable;
16    use SerializesModels;
17
18    /** @var string */
19    public $subject;
20
21    /** @var string */
22    public $msg;
23
24    /** @var string */
25    public $xml;
26
27    /**
28     * Create a new message instance.
29     *
30     * @param string $subject
31     * @param string $msg
32     * @param string $xml
33     * @return void
34     */
35    public function __construct(string $subject, string $msg, string $xml)
36    {
37        $this->subject = $subject;
38        $this->msg     = $msg;
39        $this->xml     = $xml;
40    }
41
42    /**
43     * Build the message.
44     *
45     * @return $this
46     */
47    public function build()
48    {
49        $msg = $this->msg;
50        return $this->subject(
51            sprintf(
52                "[LDC CRM] %s",
53                $this->subject
54            )
55        )->view(
56            'mail.arcadia-error',
57            compact('msg')
58        )->attachData(
59            $this->xml,
60            date("Ymd_His") . '.xml',
61            ['mime' => 'application/xml']
62        );
63    }
64}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Mail/ServiceCredentialRenewed.php.html b/tests/coverage/app/Mail/ServiceCredentialRenewed.php.html new file mode 100644 index 0000000..21f086a --- /dev/null +++ b/tests/coverage/app/Mail/ServiceCredentialRenewed.php.html @@ -0,0 +1,183 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Mail/ServiceCredentialRenewed.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ServiceCredentialRenewed
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 __construct
n/a
0 / 0
1
n/a
0 / 0
 build
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Mail;
4
5use App\Models\Service;
6use App\Models\User;
7use Illuminate\Mail\Mailable;
8
9/**
10 * @codeCoverageIgnore
11 */
12class ServiceCredentialRenewed extends Mailable
13{
14    /**
15     * The concerned service
16     *
17     * @var \App\Models\Service
18     */
19    public $service;
20
21    /**
22     * The user who did the action
23     *
24     * @var \App\Models\User
25     */
26    public $user;
27
28    /**
29     * Create a new message instance.
30     *
31     * @param Service $service
32     * @param User    $user
33     * @return void
34     */
35    public function __construct(Service $service, User $user)
36    {
37        $this->service = $service;
38        $this->user    = $user;
39    }
40
41    /**
42     * Build the message.
43     *
44     * @return $this
45     */
46    public function build()
47    {
48        return $this->view('mail.service-renewed')
49            ->subject('!!! Warning !!!, New service credential have been generated !')
50            ->text('mail.service-renewed-plain')
51            ->with('appName', config('app.name'))
52            ->with('service', $this->service)
53            ->with('by', sprintf('%s %s (%s)', $this->user->firstname, $this->user->lastname, $this->user->email))
54            ->to(
55                config('mail.contactsList.serviceweb', 'equipe.web@ldc.fr'),
56                sprintf('%s service warning', config('app.name'))
57            );
58    }
59}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Mail/dashboard.html b/tests/coverage/app/Mail/dashboard.html new file mode 100644 index 0000000..ccdab6b --- /dev/null +++ b/tests/coverage/app/Mail/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Mail + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Mail/index.html b/tests/coverage/app/Mail/index.html new file mode 100644 index 0000000..8ffbd7d --- /dev/null +++ b/tests/coverage/app/Mail/index.html @@ -0,0 +1,103 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Mail + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
CourrierArcadiaError.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ServiceCredentialRenewed.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Models/Answer.php.html b/tests/coverage/app/Models/Answer.php.html new file mode 100644 index 0000000..5571a37 --- /dev/null +++ b/tests/coverage/app/Models/Answer.php.html @@ -0,0 +1,274 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/Answer.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
Answer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
 question
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 customers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7use Illuminate\Support\Str;
8use Kwaadpepper\CrudPolicies\Enums\CrudAction;
9use Kwaadpepper\CrudPolicies\Enums\CrudType;
10use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
11
12class Answer extends Model
13{
14    use HasFactory;
15    use IsCrudModel;
16
17    /** @var string */
18    protected $crudLabelColumn = 'answer';
19
20    /** @var string */
21    protected $crudValueColumn = 'id';
22
23    /**
24     * The attributes that are mass assignable.
25     *
26     * @var string[]
27     */
28    protected $fillable = [
29        'answer'
30    ];
31
32    /**
33     * Answer
34     *
35     * @param array $attributes
36     */
37    public function __construct(array $attributes = [])
38    {
39        self::$editableProperties = [
40            'answer' => [
41                'label' => Str::ucfirst(trans('models.common.answer')),
42                'type' => CrudType::string(),
43                'validate' => [
44                    'min:1',
45                    'max:255'
46                ],
47                'actions' => [
48                    CrudAction::viewAny(),
49                    CrudAction::view(),
50                    CrudAction::create(),
51                    CrudAction::update()
52                ]
53            ]
54        ];
55        parent::__construct($attributes);
56    }
57
58    // * RELATIONS
59
60    /**
61     * Question BelongsTo relation
62     *
63     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
64     */
65    public function question()
66    {
67        return $this->belongsTo(Question::class);
68    }
69
70    /**
71     * Answer belongsToMany relation
72     *
73     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
74     */
75    public function customers()
76    {
77        return $this->belongsToMany(Customer::class, 'customer_answer');
78    }
79}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/Brand.php.html b/tests/coverage/app/Models/Brand.php.html new file mode 100644 index 0000000..b25b6bc --- /dev/null +++ b/tests/coverage/app/Models/Brand.php.html @@ -0,0 +1,520 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/Brand.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 40.00% covered (danger) +
+
+
40.00%
4 / 10
CRAP
+
+ 71.43% covered (warning) +
+
+
71.43%
20 / 28
Brand
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 40.00% covered (danger) +
+
+
40.00%
4 / 10
12.33
+
+ 71.43% covered (warning) +
+
+
71.43%
20 / 28
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
 getCacheBaseTags
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 booted
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
1.03
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
 services
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 questions
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 customerInfos
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 servicesStats
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 singleOptins
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 getAccountsAttribute
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 getTotalOptinsAttribute
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7use Illuminate\Support\Str;
8use Kwaadpepper\CrudPolicies\Enums\CrudAction;
9use Kwaadpepper\CrudPolicies\Enums\CrudType;
10use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
11use Rennokki\QueryCache\Traits\QueryCacheable;
12
13class Brand extends Model
14{
15    use IsCrudModel;
16    use HasFactory;
17    use QueryCacheable;
18
19    /** @var string */
20    protected $crudLabelColumn = 'name';
21
22    /** @var string */
23    protected $crudValueColumn = 'id';
24
25    /**
26     * The timeout cache for this model
27     *
28     * @var integer
29     */
30    protected $cacheFor = 0;
31
32    /**
33     * The attributes that are mass assignable.
34     *
35     * @var string[]
36     */
37    protected $fillable = [
38        'name'
39    ];
40
41    /**
42     * Brand
43     *
44     * @param array $attributes
45     */
46    public function __construct(array $attributes = [])
47    {
48        $this->cacheFor = config('api.servicesDBCache') * 60;
49
50        self::$editableProperties = [
51            'name' => [
52                'label' => Str::ucfirst(trans('models.common.name')),
53                'type' => CrudType::string(),
54                'validate' => [
55                    'min:5',
56                    'max:255'
57                ],
58                'actions' => [
59                    CrudAction::viewAny(),
60                    CrudAction::view(),
61                    CrudAction::create(),
62                    CrudAction::update()
63                ]
64            ]
65        ];
66        parent::__construct($attributes);
67    }
68
69    /**
70     * Set the base cache tags that will be present
71     * on all queries.
72     *
73     * ! This is used to flush caching
74     *
75     * @return array
76     */
77    protected function getCacheBaseTags(): array
78    {
79        return [
80            'crm_brands',
81        ];
82    }
83
84    /**
85     * Perform any actions required after the model boots.
86     *
87     * @return void
88     */
89    protected static function booted()
90    {
91        static::created(function () {
92            // * Flush Eloquent cache.
93            self::flushQueryCache();
94        });
95        static::updated(function () {
96            // * Flush Eloquent cache.
97            self::flushQueryCache();
98        });
99        static::deleted(function () {
100            // * Flush Eloquent cache.
101            self::flushQueryCache();
102        });
103    }
104
105    // * RELATIONS
106
107    /**
108     * Service hasMany relation
109     *
110     * @return \Illuminate\Database\Eloquent\Relations\HasMany
111     */
112    public function services()
113    {
114        return $this->hasMany(Service::class);
115    }
116
117    /**
118     * Question hasMany relation
119     *
120     * @return \Illuminate\Database\Eloquent\Relations\HasMany
121     */
122    public function questions()
123    {
124        return $this->hasMany(Question::class);
125    }
126
127    /**
128     * CustomerInfo hasMany relation
129     *
130     * @return \Illuminate\Database\Eloquent\Relations\HasMany
131     */
132    public function customerInfos()
133    {
134        return $this->hasMany(CustomerInfo::class);
135    }
136
137    /**
138     * ServiceStat hasMany relation
139     *
140     * @return \Illuminate\Database\Eloquent\Relations\HasMany
141     */
142    public function servicesStats()
143    {
144        return $this->hasMany(ServiceStat::class);
145    }
146
147    /**
148     * SingleOptin hasMany relation
149     *
150     * @return \Illuminate\Database\Eloquent\Relations\HasMany
151     */
152    public function singleOptins()
153    {
154        return $this->hasMany(SingleOptin::class);
155    }
156
157    // * METHODS
158
159    /**
160     * Account attribute accessor
161     *
162     * @return integer
163     */
164    public function getAccountsAttribute(): int
165    {
166        return $this->customerInfos()->count();
167    }
168
169    /**
170     * Total optins attribute accessor
171     *
172     * @return integer
173     */
174    public function getTotalOptinsAttribute(): int
175    {
176        return $this->customerInfos()->where('opt_in', 1)->count() + $this->singleOptins()->count();
177    }
178}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/BrandStat.php.html b/tests/coverage/app/Models/BrandStat.php.html new file mode 100644 index 0000000..a5c16a0 --- /dev/null +++ b/tests/coverage/app/Models/BrandStat.php.html @@ -0,0 +1,236 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/BrandStat.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
CRAP
+
+ 50.00% covered (danger) +
+
+
50.00%
3 / 6
BrandStat
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
4.12
+
+ 50.00% covered (danger) +
+
+
50.00%
3 / 6
 booted
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.26
+
+ 60.00% covered (warning) +
+
+
60.00%
3 / 5
 brand
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Model;
6use Illuminate\Support\Carbon;
7
8class BrandStat extends Model
9{
10    /**
11     * Indicates if the model should be timestamped.
12     *
13     * @var boolean
14     */
15    public $timestamps = false;
16
17    /**
18     * The attributes that should be cast to native types.
19     *
20     * @var array
21     */
22    public $casts = [
23        'created_at' => 'date'
24    ];
25
26    /**
27     * The attributes that are mass assignable.
28     *
29     * @var string[]
30     */
31    protected $fillable = [
32        'active', 'inactive',
33        'old_accounts', 'new_accounts',
34        'total_accounts', 'total_opt_in'
35    ];
36
37    /**
38     * Perform any actions required after the model boots.
39     *
40     * @return void
41     */
42    protected static function booted()
43    {
44        static::creating(function (BrandStat $stat) {
45            if (!$stat->created_at) {
46                $stat->created_at = Carbon::now();
47            }
48        });
49    }
50
51    // * RELATIONS
52
53    /**
54     * Brand hasMany relation
55     *
56     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
57     */
58    public function brand()
59    {
60        return $this->belongsTo(Brand::class);
61    }
62}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/Customer.php.html b/tests/coverage/app/Models/Customer.php.html new file mode 100644 index 0000000..6c25a52 --- /dev/null +++ b/tests/coverage/app/Models/Customer.php.html @@ -0,0 +1,554 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/Customer.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
CRAP
+
+ 93.22% covered (success) +
+
+
93.22%
55 / 59
Customer
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
24.18
+
+ 93.22% covered (success) +
+
+
93.22%
55 / 59
 infos
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 logins
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 optInHistory
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 brands
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 answers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 info
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6.01
+
+ 92.86% covered (success) +
+
+
92.86%
13 / 14
 login
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6.01
+
+ 92.86% covered (success) +
+
+
92.86%
13 / 14
 loadBrandData
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
5.12
+
+ 83.33% covered (warning) +
+
+
83.33%
10 / 12
 setOptIn
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 getAnswers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Builder;
6use Illuminate\Database\Eloquent\Collection;
7use Illuminate\Database\Eloquent\Factories\HasFactory;
8use Illuminate\Database\Eloquent\Model;
9use Illuminate\Database\Eloquent\ModelNotFoundException;
10
11class Customer extends Model
12{
13    use HasFactory;
14
15    public const FIRST_OR_NULL = 0;
16    public const FIRST_OR_NEW  = 1;
17    public const FIRST_OR_FAIL = 2;
18
19    /**
20     * The attributes that are mass assignable.
21     *
22     * @var string[]
23     */
24    protected $fillable = [
25        'email'
26    ];
27
28    // * RELATIONS
29
30    /**
31     * CustomerInfo hasMany relation
32     *
33     * @return \Illuminate\Database\Eloquent\Relations\HasMany
34     */
35    public function infos()
36    {
37        return $this->hasMany(CustomerInfo::class);
38    }
39
40    /**
41     * CustomerLogin hasMany relation
42     *
43     * @return \Illuminate\Database\Eloquent\Relations\HasMany
44     */
45    public function logins()
46    {
47        return $this->hasMany(CustomerLogin::class);
48    }
49
50    /**
51     * CustomerOptInHistory hasMany relation
52     *
53     * @return \Illuminate\Database\Eloquent\Relations\HasMany
54     */
55    public function optInHistory()
56    {
57        return $this->hasMany(CustomerOptInHistory::class);
58    }
59
60    /**
61     * Customer Brand belongsToMany relation
62     *
63     * @return \Illuminate\Database\Eloquent\Relations\belongsToMany
64     */
65    public function brands()
66    {
67        return $this->belongsToMany(Brand::class, CustomerInfo::class);
68    }
69
70    /**
71     * Answers belongsToMany relation
72     *
73     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
74     */
75    public function answers()
76    {
77        return $this->belongsToMany(Answer::class, 'customer_answer');
78    }
79
80    // * METHODS
81
82    /**
83     * Get the CustomerInfo from a specific brand
84     * or create a New one if $firstOrNewOrFail is 1
85     * throw model not found exception if firstOrNewOrFail = 2
86     *
87     * @param Brand   $brand
88     * @param integer $firstOrNewOrFail
89     * @return \Illuminate\Database\Eloquent\Builder
90     */
91    public function info(Brand $brand, int $firstOrNewOrFail = self::FIRST_OR_NULL): ?CustomerInfo
92    {
93        if (isset($this->relations['infos'])) {
94            /** @var \App\Models\CustomerInfo $info */
95            foreach ($this->relations['infos'] as $info) {
96                if ($info->brand_id === $brand->id) {
97                    return $info;
98                }
99            }
100        }
101        $query = CustomerInfo::whereHas('brand', function (Builder $query) use ($brand) {
102            $query->where('id', $brand->id);
103        })->whereHas('customer', function (Builder $query) {
104            $query->where('id', $this->id);
105        });
106        if ($firstOrNewOrFail === self::FIRST_OR_NEW) {
107            return $query->firstOrNew();
108        }
109        if ($firstOrNewOrFail === self::FIRST_OR_FAIL) {
110            return $query->firstOrFail();
111        }
112        return $query->first();
113    }
114
115    /**
116     * Get the CustomerLogin from a specific brand
117     * or create a New one if $firstOrNewOrFail is 1
118     * throw model not found exception if firstOrNewOrFail = 2
119     *
120     * @param Brand   $brand
121     * @param integer $firstOrNewOrFail
122     * @return \Illuminate\Database\Eloquent\Builder
123     */
124    public function login(Brand $brand, int $firstOrNewOrFail = self::FIRST_OR_NULL): ?CustomerLogin
125    {
126        if (isset($this->relations['logins'])) {
127            /** @var \App\Models\CustomerLogin $login */
128            foreach ($this->relations['logins'] as $login) {
129                if ($login->brand_id === $brand->id) {
130                    return $login;
131                }
132            }
133        }
134        $query = CustomerLogin::whereHas('brand', function (Builder $query) use ($brand) {
135            $query->where('id', $brand->id);
136        })->whereHas('customer', function (Builder $query) use ($brand) {
137            $query->where('id', $this->id);
138        });
139        if ($firstOrNewOrFail === self::FIRST_OR_NEW) {
140            return $query->firstOrNew();
141        }
142        if ($firstOrNewOrFail === self::FIRST_OR_FAIL) {
143            return $query->firstOrFail();
144        }
145        return $query->first();
146    }
147
148    /**
149     * Load login and info for this customer and this specific brand.
150     *
151     * @param Brand   $brand
152     * @param boolean $orFail
153     * @return self
154     * @throws ModelNotFoundException If no login or info was found for the brand.
155     */
156    public function loadBrandData(Brand $brand, bool $orFail = false): self
157    {
158        $this->load([
159            'logins' => function ($q) use ($brand) {
160                $q->where('brand_id', $brand->id);
161            },
162            'infos' => function ($q) use ($brand) {
163                $q->where('brand_id', $brand->id);
164            }
165        ]);
166        if ($orFail and !isset($this->relations['logins'][0])) {
167            throw (new ModelNotFoundException())->setModel(CustomerLogin::class);
168        }
169        if ($orFail and !isset($this->relations['infos'][0])) {
170            throw (new ModelNotFoundException())->setModel(CustomerInfo::class);
171        }
172        return $this;
173    }
174
175    /**
176     * Set this customer as optin
177     *
178     * @param Brand   $brand
179     * @param boolean $optIn
180     * @return void
181     * @throws \Throwable If customer history update failed.
182     */
183    public function setOptIn(Brand $brand, bool $optIn): void
184    {
185        $this->opt_in = $optIn;
186
187        // * Ajout dans la table d'historique des opt-in de la nouvelle valeur.
188        $optInHistory            = new CustomerOptInHistory();
189        $optInHistory->new_value = $optIn;
190        $optInHistory->customer()->associate($this);
191        $optInHistory->brand()->associate($brand);
192        $optInHistory->saveOrFail();
193    }
194
195    /**
196     * Get All answers given by this customer
197     * for a specific service
198     *
199     * @param Service $service
200     * @return Collection
201     */
202    public function getAnswers(Service $service): Collection
203    {
204        return Answer::whereHas('customers', function ($q) {
205            $q->whereId($this->id);
206        })->whereHas('question', function ($q) use ($service) {
207            $q->whereHas('brand', function ($q) use ($service) {
208                $q->whereId($service->brand->id);
209            });
210        })->get();
211    }
212}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/CustomerAnswer.php.html b/tests/coverage/app/Models/CustomerAnswer.php.html new file mode 100644 index 0000000..61a6835 --- /dev/null +++ b/tests/coverage/app/Models/CustomerAnswer.php.html @@ -0,0 +1,236 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/CustomerAnswer.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CustomerAnswer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 getUpdatedAtColumn
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 customer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 answer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Relations\Pivot;
6
7class CustomerAnswer extends Pivot
8{
9    /**
10     * Inhibit update_at column as we only have
11     * created_at
12     *
13     * @return null
14     */
15    public function getUpdatedAtColumn()
16    {
17        return null;
18    }
19
20    // * RELATIONS
21
22    /**
23     * Customer belongsTo relation
24     *
25     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
26     */
27    public function customer()
28    {
29        return $this->belongsTo(Customer::class);
30    }
31
32    /**
33     * Customer belongsTo relation
34     *
35     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
36     */
37    public function answer()
38    {
39        return $this->belongsTo(Answer::class);
40    }
41}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/CustomerInfo.php.html b/tests/coverage/app/Models/CustomerInfo.php.html new file mode 100644 index 0000000..49d9f92 --- /dev/null +++ b/tests/coverage/app/Models/CustomerInfo.php.html @@ -0,0 +1,357 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/CustomerInfo.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 80.00% covered (warning) +
+
+
80.00%
4 / 5
CRAP
+
+ 94.74% covered (success) +
+
+
94.74%
18 / 19
CustomerInfo
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 80.00% covered (warning) +
+
+
80.00%
4 / 5
5.00
+
+ 94.74% covered (success) +
+
+
94.74%
18 / 19
 booted
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
 customer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 login
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 getFromEmail
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use App\Enums\Country;
6use Illuminate\Support\Str;
7use Illuminate\Database\Eloquent\Factories\HasFactory;
8use Illuminate\Database\Eloquent\Model;
9use Kwaadpepper\Enum\Traits\CastsEnums;
10
11class CustomerInfo extends Model
12{
13    use HasFactory;
14    use CastsEnums;
15
16    /**
17     * Map attribute names to enum classes.
18     *
19     * @var array
20     */
21    protected $enumCasts = [
22        'country' => Country::class
23    ];
24
25    /**
26     * The attributes that should be cast to native types.
27     *
28     * @var array
29     */
30    protected $casts = [
31        'opt_in' => 'bool'
32    ];
33
34    /**
35     * The attributes that are mass assignable.
36     *
37     * @var string[]
38     */
39    protected $fillable = [
40        'firstname',
41        'lastname',
42        'birth_date',
43        'address',
44        'address2',
45        'phone',
46        'country',
47        'city',
48        'postcode',
49        'opt_in'
50    ];
51
52    /**
53     * Perform any actions required after the model boots.
54     *
55     * @return void
56     */
57    protected static function booted()
58    {
59        static::creating(function (CustomerInfo $customerInfo) {
60            $customerInfo->hashcode = Str::uuid();
61        });
62        static::deleting(function (CustomerInfo $customerInfo) {
63            CustomerOptInHistory::whereHas('customer', function ($q) use ($customerInfo) {
64                $q->whereId($customerInfo->customer->id);
65            })->whereHas('brand', function ($q) use ($customerInfo) {
66                $q->whereId($customerInfo->brand->id);
67            })->delete();
68        });
69    }
70
71    // * RELATIONS
72
73    /**
74     * Customer BelongsTo relation
75     *
76     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
77     */
78    public function customer()
79    {
80        return $this->belongsTo(Customer::class);
81    }
82
83    /**
84     * get Login
85     *
86     * @return \App\Models\CustomerLogin
87     */
88    public function login()
89    {
90        return $this->customer->login($this->brand);
91    }
92
93    /**
94     * Brand BelongsTo relation
95     *
96     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
97     */
98    public function brand()
99    {
100        return $this->belongsTo(Brand::class);
101    }
102
103    // * METHODS
104
105    /**
106     * Get customer info from email
107     *
108     * @param Brand  $brand
109     * @param string $email
110     * @return \Illuminate\Database\Eloquent\Builder
111     */
112    public static function getFromEmail(Brand $brand, string $email)
113    {
114        return CustomerInfo::whereHas('brand', function ($q) use ($brand) {
115            $q->where('id', $brand->id);
116        })->whereHas('customer', function ($q) use ($email) {
117            $q->where('email', $email);
118        });
119    }
120}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/CustomerLogin.php.html b/tests/coverage/app/Models/CustomerLogin.php.html new file mode 100644 index 0000000..012a2fa --- /dev/null +++ b/tests/coverage/app/Models/CustomerLogin.php.html @@ -0,0 +1,308 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/CustomerLogin.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
CustomerLogin
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
6
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
 booted
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
 customer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 passwordMatch
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7use Illuminate\Support\Facades\Hash;
8
9class CustomerLogin extends Model
10{
11    use HasFactory;
12
13    /**
14     * The model's attributes.
15     *
16     * @var array
17     */
18    protected $attributes = [
19        'status' => false
20    ];
21
22    /**
23     * The attributes that are mass assignable.
24     *
25     * @var string[]
26     */
27    protected $fillable = [
28        'password'
29    ];
30
31    /**
32     * The attributes that should be cast to native types.
33     *
34     * @var array
35     */
36    protected $casts = [
37        'status' => 'bool'
38    ];
39
40    /**
41     * Perform any actions required after the model boots.
42     *
43     * @return void
44     */
45    protected static function booted()
46    {
47        static::creating(function ($customerLogin) {
48            if (Hash::needsRehash($customerLogin->password)) {
49                $customerLogin->password = Hash::make(trim($customerLogin->password));
50            }
51        });
52        static::updating(function ($customerLogin) {
53            if (Hash::needsRehash($customerLogin->password)) {
54                $customerLogin->password = Hash::make(trim($customerLogin->password));
55            }
56        });
57    }
58
59    // * RELATIONS
60
61    /**
62     * Customer BelongsTo relation
63     *
64     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
65     */
66    public function customer()
67    {
68        return $this->belongsTo(Customer::class);
69    }
70
71    /**
72     * Brand BelongsTo relation
73     *
74     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
75     */
76    public function brand()
77    {
78        return $this->belongsTo(Brand::class);
79    }
80
81    // * METHODS
82    /**
83     * Check if the user password is correct
84     *
85     * @param string $password
86     * @return boolean
87     */
88    public function passwordMatch(string $password): bool
89    {
90        return Hash::check($password, $this->password);
91    }
92}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/CustomerOptInHistory.php.html b/tests/coverage/app/Models/CustomerOptInHistory.php.html new file mode 100644 index 0000000..1ff7c09 --- /dev/null +++ b/tests/coverage/app/Models/CustomerOptInHistory.php.html @@ -0,0 +1,214 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/CustomerOptInHistory.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CustomerOptInHistory
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 customer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7
8class CustomerOptInHistory extends Model
9{
10    use HasFactory;
11
12    /**
13     * The table associated with the model.
14     *
15     * @var string
16     */
17    protected $table = "customer_opt_in_history";
18
19    // * RELATIONS
20
21    /**
22     * Customer BelongsTo relation
23     *
24     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
25     */
26    public function customer()
27    {
28        return $this->belongsTo(Customer::class);
29    }
30
31    /**
32     * Brand BelongsTo relation
33     *
34     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
35     */
36    public function brand()
37    {
38        return $this->belongsTo(Brand::class);
39    }
40}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/Question.php.html b/tests/coverage/app/Models/Question.php.html new file mode 100644 index 0000000..249f1a1 --- /dev/null +++ b/tests/coverage/app/Models/Question.php.html @@ -0,0 +1,317 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/Question.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
26 / 26
Question
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
3
+
+ 100.00% covered (success) +
+
+
100.00%
26 / 26
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
24 / 24
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 answers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use App\Enums\QuestionType;
6use Illuminate\Database\Eloquent\Factories\HasFactory;
7use Illuminate\Database\Eloquent\Model;
8use Illuminate\Support\Str;
9use Kwaadpepper\CrudPolicies\Enums\CrudAction;
10use Kwaadpepper\CrudPolicies\Enums\CrudType;
11use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
12use Kwaadpepper\Enum\Traits\CastsEnums;
13
14class Question extends Model
15{
16    use CastsEnums;
17    use HasFactory;
18    use IsCrudModel;
19
20    /** @var string */
21    protected $crudLabelColumn = 'question';
22
23    /** @var string */
24    protected $crudValueColumn = 'id';
25
26    /**
27     * Map attribute names to enum classes.
28     *
29     * @var array
30     */
31    protected $enumCasts = [
32        'type' => QuestionType::class
33    ];
34
35    /**
36     * The attributes that are mass assignable.
37     *
38     * @var string[]
39     */
40    protected $fillable = [
41        'question',
42        'type'
43    ];
44
45    /**
46     * Question
47     *
48     * @param array $attributes
49     */
50    public function __construct(array $attributes = [])
51    {
52        self::$editableProperties = [
53            'question' => [
54                'label' => Str::ucfirst(trans('models.common.question')),
55                'type' => CrudType::string(),
56                'validate' => [
57                    'min:10',
58                    'max:255'
59                ],
60                'actions' => [
61                    CrudAction::viewAny(),
62                    CrudAction::view(),
63                    CrudAction::create(),
64                    CrudAction::update()
65                ]
66            ],
67            'type' => [
68                'label' => Str::ucfirst(trans('models.common.type')),
69                'type' => CrudType::enum(),
70                'enum' => QuestionType::class,
71                'actions' => [
72                    CrudAction::view(),
73                    CrudAction::create(),
74                    CrudAction::update(),
75                ]
76            ],
77            'brand_id' => [
78                'label' => Str::ucfirst(trans('models.classes.brand')),
79                'type' => CrudType::belongsTo(),
80                'belongsTo' => Brand::class,
81                'actions' => [
82                    CrudAction::viewAny(),
83                    CrudAction::view(),
84                    CrudAction::create(),
85                    CrudAction::update(),
86                ]
87            ],
88            'answers' => [
89                'label' => Str::ucfirst(Str::plural(trans('models.classes.answers'))),
90                'type' => CrudType::belongsToMany(),
91                'belongsToMany' => Answer::class,
92                'belongsToManyLabel' => 'answer',
93                'actions' => [
94                    CrudAction::view()
95                ]
96            ]
97        ];
98        parent::__construct($attributes);
99    }
100
101    // * RELATIONS
102
103    /**
104     * Brand BelongsTo relation
105     *
106     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
107     */
108    public function brand()
109    {
110        return $this->belongsTo(Brand::class);
111    }
112
113    /**
114     * Answer BelongsTo relation
115     *
116     * @return \Illuminate\Database\Eloquent\Relations\HasMany
117     */
118    public function answers()
119    {
120        return $this->hasMany(Answer::class);
121    }
122}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/Service.php.html b/tests/coverage/app/Models/Service.php.html new file mode 100644 index 0000000..221bc64 --- /dev/null +++ b/tests/coverage/app/Models/Service.php.html @@ -0,0 +1,608 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/Service.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 60.00% covered (warning) +
+
+
60.00%
6 / 10
CRAP
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
Service
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 60.00% covered (warning) +
+
+
60.00%
6 / 10
15.57
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
16 / 16
 getCacheBaseTags
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 booted
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
1.05
+
+ 64.29% covered (warning) +
+
+
64.29%
9 / 14
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 servicesStats
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 generateBearerToken
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
 generateBearerPassword
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
 incrementStat
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 getFromBearerToken
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
 authenticateApi
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use App\Jobs\SetServiceStats;
6use App\Lib\Helper;
7use Illuminate\Database\Eloquent\Factories\HasFactory;
8use Illuminate\Database\Eloquent\Model;
9use Illuminate\Database\Eloquent\ModelNotFoundException;
10use Illuminate\Http\Request;
11use Illuminate\Support\Facades\Crypt;
12use Illuminate\Support\Facades\Hash;
13use Illuminate\Support\Str;
14use Kwaadpepper\CrudPolicies\Enums\CrudAction;
15use Kwaadpepper\CrudPolicies\Enums\CrudType;
16use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
17use Rennokki\QueryCache\Traits\QueryCacheable;
18use RuntimeException;
19
20class Service extends Model
21{
22    use IsCrudModel;
23    use HasFactory;
24    use QueryCacheable;
25
26    /** @var string */
27    protected $crudLabelColumn = 'name';
28
29    /** @var string */
30    protected $crudValueColumn = 'id';
31
32    /**
33     * The timeout cache for this model
34     *
35     * @var integer
36     */
37    protected $cacheFor = 0;
38
39    /**
40     * The attributes that are mass assignable.
41     *
42     * @var string[]
43     */
44    protected $fillable = [
45        'name', 'brand_id'
46    ];
47
48    /**
49     * Service
50     *
51     * @param array $attributes
52     */
53    public function __construct(array $attributes = [])
54    {
55        $this->cacheFor = config('api.servicesDBCache') * 60;
56
57        self::$editableProperties = [
58            'name' => [
59                'label' => Str::ucfirst(trans('models.common.name')),
60                'type' => CrudType::string(),
61                'validate' => [
62                    'min:5',
63                    'max:255'
64                ],
65                'actions' => [
66                    CrudAction::viewAny(),
67                    CrudAction::view(),
68                    CrudAction::create(),
69                    CrudAction::update()
70                ]
71            ],
72            'brand_id' => [
73                'label' => Str::ucfirst(trans('models.classes.brand')),
74                'type' => CrudType::belongsTo(),
75                'belongsTo' => Brand::class,
76                'actions' => [
77                    CrudAction::viewAny(),
78                    CrudAction::view(),
79                    CrudAction::create(),
80                    CrudAction::update()
81                ]
82            ],
83        ];
84        parent::__construct($attributes);
85    }
86
87    /**
88     * Set the base cache tags that will be present
89     * on all queries.
90     *
91     * ! This is used to flush caching
92     *
93     * @return array
94     */
95    protected function getCacheBaseTags(): array
96    {
97        return [
98            'crm_services',
99        ];
100    }
101
102    /**
103     * Perform any actions required after the model boots.
104     *
105     * @return void
106     */
107    protected static function booted()
108    {
109        static::creating(function (self $service) {
110            $service->generateBearerToken();
111            $service->generateBearerPassword();
112        });
113        static::created(function () {
114            // * Flush Eloquent cache.
115            self::flushQueryCache();
116        });
117        static::updated(function () {
118            // * Flush Eloquent cache.
119            self::flushQueryCache();
120        });
121        static::deleted(function () {
122            // * Flush Eloquent cache.
123            self::flushQueryCache();
124        });
125    }
126
127    // * RELATIONS
128
129    /**
130     * Brand BelongsTo relation
131     *
132     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
133     */
134    public function brand()
135    {
136        return $this->belongsTo(Brand::class);
137    }
138
139    /**
140     * ServiceStat hasMany relation
141     *
142     * @return \Illuminate\Database\Eloquent\Relations\HasMany
143     */
144    public function servicesStats()
145    {
146        return $this->hasMany(ServiceStat::class);
147    }
148
149    // * METHODS
150
151    /**
152     * Generates new bearer token
153     *
154     * @return string
155     */
156    public function generateBearerToken(): string
157    {
158        $this->bearer_token      = hash('ripemd160', Str::uuid());
159        $this->bearer_token_date = now();
160        return $this->bearer_token;
161    }
162
163    /**
164     * Generates new bearer password
165     * It store the crypted password in model
166     * and returns the plain password
167     *
168     * @return string
169     */
170    public function generateBearerPassword(): string
171    {
172        $pwd              = hash('sha256', Str::uuid());
173        $this->bearer_pwd = Crypt::encryptString($pwd);
174        return $pwd;
175    }
176
177    /**
178     * Increment API CRM Stat
179     *
180     * @return void
181     */
182    public function incrementStat(): void
183    {
184        SetServiceStats::dispatch($this)->afterResponse()
185            ->onConnection('database')->onQueue('stats');
186    }
187
188    /**
189     * Get Service from bearerToken with its brand
190     *
191     * TODO remove this method when dropping V1
192     *
193     * @param string $bearerToken
194     * @return \App\Models\Service
195     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException If the token matches no known service.
196     */
197    public static function getFromBearerToken(string $bearerToken): self
198    {
199        $services = static::all();
200        /** @var \App\Models\Service $service */
201        foreach ($services as $service) {
202            // * Keep compatibility with V3.
203            $tokenEquals = false;
204            try {
205                $tokenEquals = Hash::check($bearerToken, $service->bearer_token);
206            } catch (RuntimeException $e) {
207                $tokenEquals = $bearerToken === $service->bearer_token;
208            }
209            if ($service->bearer_token and $tokenEquals) {
210                return $service->load([
211                    'brand' => function ($query) use ($service) {
212                        return $query->cacheTags(["service:{$service->id}:brand"]);
213                    },
214                ]);
215            }
216        }
217        throw new ModelNotFoundException('Could not find service from Service Token');
218    }
219
220    /**
221     * Authenticate the request for this service
222     *
223     * This will check hash-mac and that the request is wellformed
224     *
225     * The request should have the minimum following parameters
226     *  - timestamp using format Y-m-d H:i:s, and must be UTC (Greenwich Mean Time)
227     *  - bearerToken the public token generated by the crm
228     *  - signature the hashmac signature of the request
229     *
230     * ! Signature must be calculated with every parameter urlencode once only
231     * ! All parameters to calculate the signature must be sorted (natural sort asc)
232     *
233     * ! Signature should look like
234     * * httpVerb\n
235     * * uri\n
236     * * queryString
237     *
238     * ! httpVerb as well as uri must be lower case
239     * ! uri should include scheme, host and path and nothing else, if path is empty a trailing slash should be added
240     * ! eg: https://ldc-crm.fr/customer/1  or  https://ldc-crm.fr/
241     * ! The query string shall be separated with '&', eg param1=value1&param2=value2
242     * ! Key names and values of the query string must be url encoded using RFC 3986
243     * ! http://www.faqs.org/rfcs/rfc3986.html
244     * ! Signature must be hmac sha256 using the private key generatedby the crm
245     *
246     *     * ! They should then be concat in a string separated by \n
247     *
248     *
249     *
250     * @param \Illuminate\Http\Request $request
251     * @return boolean
252     * @throws \App\Exceptions\Api\V2\CrmException If an error occurs.
253     */
254    public function authenticateApi(Request $request): bool
255    {
256        $calculatedHash = Helper::signRequest(
257            $this->bearer_pwd,
258            $request->getUri(),
259            $request->getMethod(),
260            $request->all()
261        );
262
263        // ! Très important hash_equals https://fr.wikipedia.org/wiki/Attaque_temporelle
264        return \hash_equals($calculatedHash, $request->signature);
265    }
266}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/ServiceStat.php.html b/tests/coverage/app/Models/ServiceStat.php.html new file mode 100644 index 0000000..9fc1261 --- /dev/null +++ b/tests/coverage/app/Models/ServiceStat.php.html @@ -0,0 +1,223 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/ServiceStat.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
ServiceStat
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 service
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7
8class ServiceStat extends Model
9{
10    use HasFactory;
11
12    /**
13     * The attributes that are mass assignable.
14     *
15     * @var string[]
16     */
17    protected $fillable = ['date'];
18
19    /**
20     * The model's attributes.
21     *
22     * @var array
23     */
24    protected $attributes = [
25        'nb_calls' => 0
26    ];
27
28    // * RELATIONS
29
30    /**
31     * Brand BelongsTo relation
32     *
33     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
34     */
35    public function brand()
36    {
37        return $this->belongsTo(Brand::class);
38    }
39
40    /**
41     * Service BelongsTo relation
42     *
43     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
44     */
45    public function service()
46    {
47        return $this->belongsTo(Service::class);
48    }
49}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/SingleOptin.php.html b/tests/coverage/app/Models/SingleOptin.php.html new file mode 100644 index 0000000..fcc802c --- /dev/null +++ b/tests/coverage/app/Models/SingleOptin.php.html @@ -0,0 +1,274 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/SingleOptin.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 50.00% covered (danger) +
+
+
50.00%
1 / 2
CRAP
+
+ 79.31% covered (warning) +
+
+
79.31%
23 / 29
SingleOptin
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 50.00% covered (danger) +
+
+
50.00%
1 / 2
4.14
+
+ 79.31% covered (warning) +
+
+
79.31%
23 / 29
 __construct
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.09
+
+ 78.57% covered (warning) +
+
+
78.57%
22 / 28
 brand
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7use Illuminate\Support\Str;
8use Kwaadpepper\CrudPolicies\Enums\CrudAction;
9use Kwaadpepper\CrudPolicies\Enums\CrudType;
10use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
11
12class SingleOptin extends Model
13{
14    use HasFactory;
15    use IsCrudModel;
16
17    /** @var string */
18    protected $crudLabelColumn = 'email';
19
20    /** @var string */
21    protected $crudValueColumn = 'id';
22
23    /**
24     * The attributes that are mass assignable.
25     *
26     * @var string[]
27     */
28    protected $fillable = [
29        'email'
30    ];
31
32    /**
33     * SingleOptin
34     *
35     * @param array $attributes
36     */
37    public function __construct(array $attributes = [])
38    {
39        self::$editableProperties = [
40            'email' => [
41                'label' => Str::ucfirst(trans('models.common.email')),
42                'type' => CrudType::email(),
43                'validate' => [
44                    'required',
45                    'email:rfc,dns',
46                    // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
47                    function ($attribute, $value, $fail) {
48                        $q = static::query();
49                        if (request()->user) {
50                            $q = $q->where('id', '!=', $this->id);
51                        }
52                        if ($q->where('email', $value)->exists()) {
53                            $fail(__(':attribute est déjà utilisé', [
54                                'attribute' => $attribute
55                            ]));
56                        }
57                    }
58                ],
59                'actions' => [
60                    CrudAction::viewAny(),
61                    CrudAction::view(),
62                    CrudAction::create(),
63                    CrudAction::update()
64                ]
65            ],
66            'brand_id' => [
67                'label' => Str::ucfirst(trans('models.classes.brand')),
68                'type' => CrudType::belongsTo(),
69                'belongsTo' => Brand::class,
70                'actions' => [
71                    CrudAction::viewAny(),
72                    CrudAction::view()
73                ]
74            ],
75            'created_at' => [
76                'label' => Str::ucfirst(trans('models.common.optinDate')),
77                'type' => CrudType::string(),
78                'readonly' => true,
79                'actions' => [
80                    CrudAction::viewAny(),
81                    CrudAction::view(),
82                    CrudAction::update()
83                ]
84            ],
85        ];
86        parent::__construct($attributes);
87    }
88
89    // * RELATIONS
90
91    /**
92     * Brand BelongsTo relation
93     *
94     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
95     */
96    public function brand()
97    {
98        return $this->belongsTo(Brand::class);
99    }
100}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/User.php.html b/tests/coverage/app/Models/User.php.html new file mode 100644 index 0000000..6ad8539 --- /dev/null +++ b/tests/coverage/app/Models/User.php.html @@ -0,0 +1,145 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models/User.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
User
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Foundation\Auth\User as Authenticatable;
7use Illuminate\Notifications\Notifiable;
8
9class User extends Authenticatable
10{
11    use HasFactory;
12    use Notifiable;
13
14    /**
15     * The attributes that are mass assignable.
16     *
17     * @var array
18     */
19    protected $fillable = [
20        'name',
21        'email',
22        'password',
23    ];
24
25    /**
26     * The attributes that should be hidden for arrays.
27     *
28     * @var array
29     */
30    protected $hidden = [
31        'password',
32        'remember_token',
33    ];
34
35    /**
36     * The attributes that should be cast to native types.
37     *
38     * @var array
39     */
40    protected $casts = [
41        'email_verified_at' => 'datetime',
42    ];
43}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Models/dashboard.html b/tests/coverage/app/Models/dashboard.html new file mode 100644 index 0000000..a41004f --- /dev/null +++ b/tests/coverage/app/Models/dashboard.html @@ -0,0 +1,310 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Models + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + + +
ClassCoverage
App\Models\BrandStat50%
App\Models\Brand71%
App\Models\SingleOptin79%
App\Models\Service80%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + + +
ClassCRAP
App\Models\Service15
App\Models\SingleOptin4
App\Models\BrandStat4
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+
+

Project Risks

+
+ + + + + + + + + + + + + +
MethodCRAP
loadBrandData5
__construct3
booted2
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Models/index.html b/tests/coverage/app/Models/index.html new file mode 100644 index 0000000..ce2f94d --- /dev/null +++ b/tests/coverage/app/Models/index.html @@ -0,0 +1,441 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Models + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 86.77% covered (warning) +
+
+
86.77%
223 / 257
+
+ 69.64% covered (warning) +
+
+
69.64%
39 / 56
+
+ 50.00% covered (danger) +
+
+
50.00%
6 / 12
Answer.php
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Brand.php
+
+ 71.43% covered (warning) +
+
+
71.43%
20 / 28
+
+ 40.00% covered (danger) +
+
+
40.00%
4 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
BrandStat.php
+
+ 50.00% covered (danger) +
+
+
50.00%
3 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
Customer.php
+
+ 93.22% covered (success) +
+
+
93.22%
55 / 59
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CustomerAnswer.php
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CustomerInfo.php
+
+ 94.74% covered (success) +
+
+
94.74%
18 / 19
+
+ 80.00% covered (warning) +
+
+
80.00%
4 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CustomerLogin.php
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CustomerOptInHistory.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Question.php
+
+ 100.00% covered (success) +
+
+
100.00%
26 / 26
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Service.php
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
+
+ 60.00% covered (warning) +
+
+
60.00%
6 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ServiceStat.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
SingleOptin.php
+
+ 79.31% covered (warning) +
+
+
79.31%
23 / 29
+
+ 50.00% covered (danger) +
+
+
50.00%
1 / 2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
User.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Policies/AnswerPolicy.php.html b/tests/coverage/app/Policies/AnswerPolicy.php.html new file mode 100644 index 0000000..24a60f6 --- /dev/null +++ b/tests/coverage/app/Policies/AnswerPolicy.php.html @@ -0,0 +1,146 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Policies/AnswerPolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AnswerPolicy
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Policies\Rules\UserStaticRules as USR;
6use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
7
8/**
9 * @codeCoverageIgnore
10 */
11class AnswerPolicy extends RootPolicy
12{
13    /**
14     * Init policies on call
15     *
16     * @return void
17     */
18    public function setPolicies()
19    {
20        /**
21         * This first rule to return true in a policy action authorises the action
22         */
23        $this->policies = [
24            'answer' => [
25                'viewAny' => USR::make('everyOne'),
26                'view' => USR::make('noOne'),
27                'update' => USR::make('everyOne'),
28                'create' => USR::make('everyOne'),
29                'delete' => USR::make('everyOne')
30            ]
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Policies/BrandPolicy.php.html b/tests/coverage/app/Policies/BrandPolicy.php.html new file mode 100644 index 0000000..72c1505 --- /dev/null +++ b/tests/coverage/app/Policies/BrandPolicy.php.html @@ -0,0 +1,146 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Policies/BrandPolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
BrandPolicy
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Policies\Rules\UserStaticRules as USR;
6use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
7
8/**
9 * @codeCoverageIgnore
10 */
11class BrandPolicy extends RootPolicy
12{
13    /**
14     * Init policies on call
15     *
16     * @return void
17     */
18    public function setPolicies()
19    {
20        /**
21         * This first rule to return true in a policy action authorises the action
22         */
23        $this->policies = [
24            'brand' => [
25                'viewAny' => USR::make('everyOne'),
26                'view' => USR::make('noOne'),
27                'update' => USR::make('everyOne'),
28                'create' => USR::make('everyOne'),
29                'delete' => USR::make('noOne')
30            ]
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Policies/CustomerPolicy.php.html b/tests/coverage/app/Policies/CustomerPolicy.php.html new file mode 100644 index 0000000..fa17e60 --- /dev/null +++ b/tests/coverage/app/Policies/CustomerPolicy.php.html @@ -0,0 +1,174 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Policies/CustomerPolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CustomerPolicy
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
 customerBelongsToService
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Models\Customer;
6use App\Models\Service;
7use App\Policies\Rules\UserStaticRules as USR;
8use Illuminate\Auth\Access\HandlesAuthorization;
9use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
10
11/**
12 * @codeCoverageIgnore
13 */
14class CustomerPolicy extends RootPolicy
15{
16    use HandlesAuthorization;
17
18    /**
19     * Init policies on call
20     *
21     * @return void
22     */
23    public function setPolicies()
24    {
25        /**
26         * This first rule to return true in a policy action authorises the action
27         */
28        $this->policies = [
29            'customer' => [
30                'viewAny' => USR::make('everyOne'),
31                'view' => USR::make('everyOne'),
32                'create' => USR::make('noOne'),
33                'update' => USR::make('noOne'),
34                'delete' => USR::make('everyOne')
35            ]
36        ];
37    }
38
39    /**
40     * Check if the customer belongs to the service
41     *
42     * @param Service  $service
43     * @param Customer $customer
44     * @return boolean
45     */
46    public function customerBelongsToService(Service $service, Customer $customer)
47    {
48        return !\is_null($customer->login($service->brand));
49    }
50}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Policies/QuestionPolicy.php.html b/tests/coverage/app/Policies/QuestionPolicy.php.html new file mode 100644 index 0000000..b4422f6 --- /dev/null +++ b/tests/coverage/app/Policies/QuestionPolicy.php.html @@ -0,0 +1,171 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Policies/QuestionPolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
QuestionPolicy
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
 questionBelongsToService
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Models\Question;
6use App\Models\Service;
7use App\Policies\Rules\UserStaticRules as USR;
8use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
9
10/**
11 * @codeCoverageIgnore
12 */
13class QuestionPolicy extends RootPolicy
14{
15    /**
16     * Init policies on call
17     *
18     * @return void
19     */
20    public function setPolicies()
21    {
22        /**
23         * This first rule to return true in a policy action authorises the action
24         */
25        $this->policies = [
26            'question' => [
27                'viewAny' => USR::make('everyOne'),
28                'view' => USR::make('everyOne'),
29                'update' => USR::make('everyOne'),
30                'create' => USR::make('everyOne'),
31                'delete' => USR::make('everyOne')
32            ]
33        ];
34    }
35
36    /**
37     * Check if the question belongs to the service
38     *
39     * @param Service  $service
40     * @param Question $question
41     * @return boolean
42     */
43    public function questionBelongsToService(Service $service, Question $question)
44    {
45        return $service->brand->id === $question->brand_id;
46    }
47}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Policies/Rules/UserStaticRules.php.html b/tests/coverage/app/Policies/Rules/UserStaticRules.php.html new file mode 100644 index 0000000..0c3e77a --- /dev/null +++ b/tests/coverage/app/Policies/Rules/UserStaticRules.php.html @@ -0,0 +1,157 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Policies/Rules/UserStaticRules.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
UserStaticRules
n/a
0 / 0
n/a
0 / 0
2
n/a
0 / 0
 everyOne
n/a
0 / 0
1
n/a
0 / 0
 noOne
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies\Rules;
4
5use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
6
7/**
8 * Userlevel Policiy Static Rules
9 * ! Rules has to return boolean value
10 * @codeCoverageIgnore
11 */
12abstract class UserStaticRules extends StaticRules
13{
14    /**
15     * Allow everyone
16     *
17     * @return boolean
18     */
19    protected static function everyOne(): bool
20    {
21        return true;
22    }
23    /**
24     * Allow no one
25     *
26     * @return boolean
27     */
28    protected static function noOne(): bool
29    {
30        return false;
31    }
32}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Policies/Rules/dashboard.html b/tests/coverage/app/Policies/Rules/dashboard.html new file mode 100644 index 0000000..cfe59ce --- /dev/null +++ b/tests/coverage/app/Policies/Rules/dashboard.html @@ -0,0 +1,286 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Policies/Rules + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Policies/Rules/index.html b/tests/coverage/app/Policies/Rules/index.html new file mode 100644 index 0000000..3d45c26 --- /dev/null +++ b/tests/coverage/app/Policies/Rules/index.html @@ -0,0 +1,91 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Policies/Rules + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
UserStaticRules.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Policies/ServicePolicy.php.html b/tests/coverage/app/Policies/ServicePolicy.php.html new file mode 100644 index 0000000..196d6d4 --- /dev/null +++ b/tests/coverage/app/Policies/ServicePolicy.php.html @@ -0,0 +1,146 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Policies/ServicePolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ServicePolicy
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Policies\Rules\UserStaticRules as USR;
6use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
7
8/**
9 * @codeCoverageIgnore
10 */
11class ServicePolicy extends RootPolicy
12{
13    /**
14     * Init policies on call
15     *
16     * @return void
17     */
18    public function setPolicies()
19    {
20        /**
21         * This first rule to return true in a policy action authorises the action
22         */
23        $this->policies = [
24            'service' => [
25                'viewAny' => USR::make('everyOne'),
26                'view' => USR::make('everyOne'),
27                'update' => USR::make('everyOne'),
28                'create' => USR::make('everyOne'),
29                'delete' => USR::make('noOne')
30            ]
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Policies/SingleOptinPolicy.php.html b/tests/coverage/app/Policies/SingleOptinPolicy.php.html new file mode 100644 index 0000000..f8d8294 --- /dev/null +++ b/tests/coverage/app/Policies/SingleOptinPolicy.php.html @@ -0,0 +1,146 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Policies/SingleOptinPolicy.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
SingleOptinPolicy
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 setPolicies
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Policies;
4
5use App\Policies\Rules\UserStaticRules as USR;
6use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
7
8/**
9 * @codeCoverageIgnore
10 */
11class SingleOptinPolicy extends RootPolicy
12{
13    /**
14     * Init policies on call
15     *
16     * @return void
17     */
18    public function setPolicies()
19    {
20        /**
21         * This first rule to return true in a policy action authorises the action
22         */
23        $this->policies = [
24            'singleoptin' => [
25                'viewAny' => USR::make('everyOne'),
26                'view' => USR::make('noOne'),
27                'create' => USR::make('noOne'),
28                'update' => USR::make('noOne'),
29                'delete' => USR::make('everyOne')
30            ]
31        ];
32    }
33}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Policies/dashboard.html b/tests/coverage/app/Policies/dashboard.html new file mode 100644 index 0000000..cc035ed --- /dev/null +++ b/tests/coverage/app/Policies/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Policies + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Policies/index.html b/tests/coverage/app/Policies/index.html new file mode 100644 index 0000000..cbe2cef --- /dev/null +++ b/tests/coverage/app/Policies/index.html @@ -0,0 +1,168 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Policies + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Rules
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
AnswerPolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
BrandPolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
CustomerPolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
QuestionPolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ServicePolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
SingleOptinPolicy.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/tests/coverage/app/Providers/AppServiceProvider.php.html b/tests/coverage/app/Providers/AppServiceProvider.php.html new file mode 100644 index 0000000..615fbb8 --- /dev/null +++ b/tests/coverage/app/Providers/AppServiceProvider.php.html @@ -0,0 +1,185 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Providers/AppServiceProvider.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AppServiceProvider
n/a
0 / 0
n/a
0 / 0
4
n/a
0 / 0
 boot
n/a
0 / 0
4
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Providers;
4
5use Illuminate\Cache\RateLimiting\Limit;
6use Illuminate\Http\Resources\Json\JsonResource;
7use Illuminate\Pagination\Paginator;
8use Illuminate\Support\Carbon;
9use Illuminate\Support\Facades\App;
10use Illuminate\Support\Facades\Cache;
11use Illuminate\Support\Facades\File;
12use Illuminate\Support\Facades\RateLimiter;
13use Illuminate\Support\Facades\URL;
14use Illuminate\Support\Facades\View;
15use Illuminate\Support\ServiceProvider;
16
17/**
18 * @codeCoverageIgnore
19 */
20class AppServiceProvider extends ServiceProvider
21{
22    /**
23     * Bootstrap any application services.
24     *
25     * @return void
26     */
27    public function boot()
28    {
29        // * STATS jobs Rate Limiting.
30        RateLimiter::for('stats', function () {
31            return Limit::perMinute(60)->by(2);
32        });
33
34        // ! Important pourra etre retiré quand la V1 sera supprimé
35        // empeche l'encapsulation dans data
36        JsonResource::withoutWrapping();
37
38        // * LOCAL HTTPS FIX.
39        if (!in_array(config('app.env'), ['local', 'testing'])) {
40            URL::forceScheme('https');
41        }
42        // * HA PROXY FIX.
43        App::singleton('clientIp', function () {
44            if (
45                config('app.env') === 'local'
46            ) {
47                return request()->getClientIp();
48            } else {
49                return request()->server('HTTP_X_FORWARDED_FOR') ??
50                request()->getClientIp();
51            }
52        });
53        // * LOCAL FORCE eg: for Carbon.
54        setlocale(LC_ALL, \sprintf(
55            '%s_%s.UTF-8',
56            config('app.locale'),
57            \strtoupper(config('app.locale'))
58        ));
59        Carbon::setLocale(config('app.locale'));
60        // * FORCE BOOTSTRAP PAGINATOR.
61        if (!app()->runningInConsole()) {
62            Paginator::useBootstrap();
63        }
64
65        $appInfos = Cache::remember('composer', 360, function () {
66            return json_decode(File::get(\app_path('../composer.json')));
67        });
68        View::share('name', $appInfos->name);
69        View::share('version', $appInfos->version);
70        View::share('license', $appInfos->license);
71    }
72}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Providers/AuthServiceProvider.php.html b/tests/coverage/app/Providers/AuthServiceProvider.php.html new file mode 100644 index 0000000..9197a22 --- /dev/null +++ b/tests/coverage/app/Providers/AuthServiceProvider.php.html @@ -0,0 +1,229 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Providers/AuthServiceProvider.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
AuthServiceProvider
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
 boot
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Providers;
4
5use App\Lib\Helper;
6use App\Models\Service;
7use Illuminate\Database\Eloquent\ModelNotFoundException;
8use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
9use Illuminate\Http\Request;
10use Illuminate\Support\Facades\Auth;
11
12class AuthServiceProvider extends ServiceProvider
13{
14    /**
15     * The policy mappings for the application.
16     *
17     * @var array
18     */
19    protected $policies = [];
20
21    /**
22     * Register any authentication / authorization services.
23     *
24     * @return void
25     */
26    public function boot()
27    {
28        $this->registerPolicies();
29
30        Auth::viaRequest('api-hmac', function (Request $request) {
31
32            /**
33             * Un token devrais être fourni pour chaque requete
34             */
35            if (is_null($request->bearer_token)) {
36                return null;
37            }
38
39            // * Si la route actuelle n'as pas de nom alors on refuse de la traiter.
40            if (!(Helper::getRoute($request))) {
41                // @codeCoverageIgnoreStart
42                return null;
43                // @codeCoverageIgnoreEnd
44            }
45
46            $service = null;
47
48            try {
49                // * On récupère le service actuel correspondant au token fourni.
50                $service = Service::getFromBearerToken($request->bearer_token ?? '');
51            } catch (ModelNotFoundException $e) {
52                return null;
53            }
54
55            // * Preload the brand as we will access it often later.
56            $service = $service->load('brand');
57
58            /**
59             ** All ApiV2 routes must pass Brand model to controller action since a Brand owns
60             ** a service token, otherwise the api cannot be called.
61             ** This hack is a custom binder instead of passing
62             ** this to route as a parameter
63             */
64            app()->bind(Service::class, function () use ($service) {
65                return $service;
66            });
67
68            // * On authenticate la request en vérifiant sa signature.
69            if ($service->authenticateApi($request)) {
70                return $service;
71            }
72
73            return null;
74        });
75    }
76}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Providers/EventServiceProvider.php.html b/tests/coverage/app/Providers/EventServiceProvider.php.html new file mode 100644 index 0000000..52516b1 --- /dev/null +++ b/tests/coverage/app/Providers/EventServiceProvider.php.html @@ -0,0 +1,126 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Providers/EventServiceProvider.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
EventServiceProvider
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Providers;
4
5use Illuminate\Auth\Events\Registered;
6use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
7use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
8
9/**
10 * codeCoverageIgnore
11 */
12class EventServiceProvider extends ServiceProvider
13{
14    /**
15     * The event listener mappings for the application.
16     *
17     * @var array
18     */
19    protected $listen = [
20        Registered::class => [
21            SendEmailVerificationNotification::class,
22        ],
23    ];
24}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Providers/RouteServiceProvider.php.html b/tests/coverage/app/Providers/RouteServiceProvider.php.html new file mode 100644 index 0000000..e2002b2 --- /dev/null +++ b/tests/coverage/app/Providers/RouteServiceProvider.php.html @@ -0,0 +1,281 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Providers/RouteServiceProvider.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
33 / 33
RouteServiceProvider
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
7
+
+ 100.00% covered (success) +
+
+
100.00%
33 / 33
 boot
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
26 / 26
 configureRateLimiting
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace App\Providers;
4
5use App\Models\Answer;
6use App\Models\Brand;
7use App\Models\Customer;
8use App\Models\CustomerInfo;
9use App\Models\Question;
10use App\Models\Service;
11use App\Models\SingleOptin;
12use Illuminate\Cache\RateLimiting\Limit;
13use Illuminate\Database\Eloquent\ModelNotFoundException;
14use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
15use Illuminate\Http\Request;
16use Illuminate\Support\Facades\RateLimiter;
17use Illuminate\Support\Facades\Route;
18
19class RouteServiceProvider extends ServiceProvider
20{
21    /**
22     * The path to the "home" route for your application.
23     *
24     * This is used by Laravel authentication to redirect users after login.
25     *
26     * @var string
27     */
28    public const HOME = '/customers';
29
30    /**
31     * Define your route model bindings, pattern filters, etc.
32     *
33     * @return void
34     */
35    public function boot()
36    {
37        $this->configureRateLimiting();
38
39        $this->routes(function () {
40            // @codeCoverageIgnoreStart
41            Route::prefix('api')->middleware('api')->namespace($this->namespace)->group(base_path('routes/api.php'));
42            Route::middleware('web')->namespace($this->namespace)->group(base_path('routes/web.php'));
43            // @codeCoverageIgnoreEnd
44        });
45
46        Route::model('brand', Brand::class);
47        Route::model('service', Service::class);
48        Route::model('single_optin', SingleOptin::class);
49        Route::model('question', Question::class);
50        Route::model('answer', Answer::class);
51        Route::model('customer', Customer::class);
52
53        // * Api parameters Bind.
54        Route::model('serviceQuestion', Question::class);
55        Route::bind('serviceCustomer', function ($value) {
56            // * Load brand
57            /** @var \App\Models\Brand $brand */
58            $brand = app(Service::class)->brand;
59
60            // ? Parameter is email
61            if (strpos($value, '@') !== false) {
62                $customer = Customer::whereHas('brands', function ($q) use ($brand) {
63                    $q->where('brands.id', '=', $brand->id);
64                })->where('email', '=', $value)->firstOrFail();
65            }
66
67            try {
68                // ? Have we found a customer yet
69                if (!isset($customer) or \get_class($customer) !== Customer::class) {
70                    // ? Parameter is hashcode
71                    $customer = CustomerInfo::whereHas('brand', function ($q) use ($brand) {
72                        $q->where('brands.id', '=', $brand->id);
73                    })->where('hashcode', $value)
74                        ->with('customer')->firstOrFail()->customer;
75                }
76
77                // * Load Customer Login and Info for this brand.
78                /** @var \App\Models\Customer $customer */
79
80                return $customer->loadBrandData($brand);
81            } catch (ModelNotFoundException $e) {
82                // * Uniform Not found exception using model Customer.
83                throw (new ModelNotFoundException())->setModel(Customer::class);
84            }
85        });
86    }
87
88    /**
89     * Configure the rate limiters for the application.
90     *
91     * @return void
92     */
93    protected function configureRateLimiting()
94    {
95        RateLimiter::for('web', function (Request $request) {
96            // @codeCoverageIgnoreStart
97            return Limit::perMinute(25)
98                ->by(optional($request->user())->id ?: app('clientIp'));
99            // @codeCoverageIgnoreEnd
100        });
101        // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
102        RateLimiter::for('api', function (Request $request) {
103            return Limit::perMinute(config('api.apiRequestRatePerMinute', 100))
104            ->by(app('clientIp'));
105        });
106    }
107}
+ + + +
+ + + + + + diff --git a/tests/coverage/app/Providers/dashboard.html b/tests/coverage/app/Providers/dashboard.html new file mode 100644 index 0000000..4b7fa1c --- /dev/null +++ b/tests/coverage/app/Providers/dashboard.html @@ -0,0 +1,285 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app/Providers + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
ClassCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + +
MethodCoverage
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/app/Providers/index.html b/tests/coverage/app/Providers/index.html new file mode 100644 index 0000000..932eb5d --- /dev/null +++ b/tests/coverage/app/Providers/index.html @@ -0,0 +1,174 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app/Providers + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
51 / 51
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
AppServiceProvider.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
AuthServiceProvider.php
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
EventServiceProvider.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
RouteServiceProvider.php
+
+ 100.00% covered (success) +
+
+
100.00%
33 / 33
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/app/dashboard.html b/tests/coverage/app/dashboard.html new file mode 100644 index 0000000..d19d743 --- /dev/null +++ b/tests/coverage/app/dashboard.html @@ -0,0 +1,329 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/app + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+ +
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+ +
+

Project Risks

+ +
+
+ +
+ + + + + + diff --git a/tests/coverage/app/index.html b/tests/coverage/app/index.html new file mode 100644 index 0000000..119718f --- /dev/null +++ b/tests/coverage/app/index.html @@ -0,0 +1,311 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/app + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 93.57% covered (success) +
+
+
93.57%
815 / 871
+
+ 82.09% covered (warning) +
+
+
82.09%
110 / 134
+
+ 78.00% covered (warning) +
+
+
78.00%
39 / 50
Console
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Enums
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
Exceptions
+
+ 88.00% covered (warning) +
+
+
88.00%
44 / 50
+
+ 62.50% covered (warning) +
+
+
62.50%
5 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
Http
+
+ 99.10% covered (success) +
+
+
99.10%
442 / 446
+
+ 96.15% covered (success) +
+
+
96.15%
50 / 52
+
+ 92.86% covered (success) +
+
+
92.86%
26 / 28
Jobs
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Lib
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
Mail
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Models
+
+ 86.77% covered (warning) +
+
+
86.77%
223 / 257
+
+ 69.64% covered (warning) +
+
+
69.64%
39 / 56
+
+ 50.00% covered (danger) +
+
+
50.00%
6 / 12
Policies
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Providers
+
+ 100.00% covered (success) +
+
+
100.00%
51 / 51
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ +
+ + diff --git a/tests/coverage/clients/V2/dashboard.html b/tests/coverage/clients/V2/dashboard.html new file mode 100644 index 0000000..1e2ceb6 --- /dev/null +++ b/tests/coverage/clients/V2/dashboard.html @@ -0,0 +1,434 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/clients/V2 + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+ +
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+ + +
+ +
+ + + + + + diff --git a/tests/coverage/clients/V2/index.html b/tests/coverage/clients/V2/index.html new file mode 100644 index 0000000..036190c --- /dev/null +++ b/tests/coverage/clients/V2/index.html @@ -0,0 +1,120 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2 + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 67.67% covered (warning) +
+
+
67.67%
720 / 1064
+
+ 47.77% covered (danger) +
+
+
47.77%
75 / 157
+
+ 30.77% covered (danger) +
+
+
30.77%
8 / 26
src
+
+ 67.67% covered (warning) +
+
+
67.67%
720 / 1064
+
+ 47.77% covered (danger) +
+
+
47.77%
75 / 157
+
+ 30.77% covered (danger) +
+
+
30.77%
8 / 26
+
+ +
+ + diff --git a/tests/coverage/clients/V2/src/CrmApiV2.php.html b/tests/coverage/clients/V2/src/CrmApiV2.php.html new file mode 100644 index 0000000..aa881b0 --- /dev/null +++ b/tests/coverage/clients/V2/src/CrmApiV2.php.html @@ -0,0 +1,383 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/CrmApiV2.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
CrmApiV2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
7
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 ping
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 getCustomerOrNew
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 getCustomer
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setStatus
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 login
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM;
4
5use LDC\LDCCRM\Enums\ApiStatus;
6use LDC\LDCCRM\Models\Customer;
7use LDC\LDCCRM\Models\SingleOptin;
8use LDC\LDCCRM\Transport\ApiInterface;
9
10/**
11 * This is the main class to interact with the CRM
12 *
13 * @package LDCCrmClient
14 */
15class CrmApiV2
16{
17    /**
18     * The Api object that will handle requests and responses
19     *
20     * @var \LDC\LDCCRM\Transport\ApiInterface
21     */
22    private static $apiInterface;
23
24    /**
25     * CrmApiV2
26     *
27     * @param string|null $endpoint
28     * @param string|null $bearerToken
29     * @param string|null $bearerPwd
30     */
31    public function __construct(string $endpoint = null, string $bearerToken = null, string $bearerPwd = null)
32    {
33        self::$apiInterface = self::$apiInterface ?: new ApiInterface(
34            $endpoint,
35            $bearerToken,
36            $bearerPwd
37        );
38        return $this;
39    }
40
41    /**
42     * Use this the ping the crm and know its current status
43     *
44     * @return boolean
45     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs
46     * on server side or on getting response.
47     */
48    public function ping(): bool
49    {
50        $request  = Utils::getResquest('apiV2.ping');
51        $response = self::$apiInterface->call($request);
52        return $response->status->equals(ApiStatus::OK());
53    }
54
55    /**
56     *
57     * Get a customer from its hashcode or email
58     * If it does not exists a new instance
59     * will be returned. You would have
60     * to call its save method later on.
61     *
62     * @param string $identification The customer hashcode or email.
63     * @return \LDC\LDCCRM\Models\Customer A client Customer model.
64     *
65     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
66     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
67     * @phpcs:ignore Squiz.Commenting.FunctionComment.EmptyThrows
68     */
69    public function getCustomerOrNew(string $identification): Customer
70    {
71        return $this->getCustomer($identification) ?? new Customer();
72    }
73
74    /**
75     * Get a customer from its hashcode or email
76     * If it does not exists a new instance
77     * will be returned. You would have
78     * to call its save method later on.
79     *
80     * @param string $identification The customer hashcode or email.
81     * @return \LDC\LDCCRM\Models\Customer|null Return null if the customer was not found
82     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
83     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
84     * Possibles are : - modelNotFound, If this customer hashcode could not be found.
85     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
86     */
87    public function getCustomer(string $identification): ?Customer
88    {
89        return Customer::find($identification);
90    }
91
92    /**
93     * Set the single optin status for an email
94     *
95     * @param string  $email
96     * @param boolean $opt_in
97     * @return boolean true if succeeded
98     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
99     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
100     * Possibles are : - singleOptinCustomerExists, If a customer exists with this email.
101     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
102     */
103    public static function setStatus(string $email, bool $opt_in): bool
104    {
105        return SingleOptin::setStatus($email, $opt_in);
106    }
107
108    /**
109     * Test a customer credentials to see if it can login
110     *
111     * @param string $email
112     * @param string $password
113     * @return boolean
114     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
115     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
116     * Possibles are : - authFailed, If credentials are not valid.
117     *                 - customerNotActivated, If the account is not activated.
118     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
119     */
120    public static function login(string $email, string $password): bool
121    {
122        return Customer::login($email, $password);
123    }
124}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Enums/ApiError.php.html b/tests/coverage/clients/V2/src/Enums/ApiError.php.html new file mode 100644 index 0000000..0baeeb4 --- /dev/null +++ b/tests/coverage/clients/V2/src/Enums/ApiError.php.html @@ -0,0 +1,290 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Enums/ApiError.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
ApiError
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 labels
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Enums;
4
5use Kwaadpepper\Enum\BaseEnum;
6
7/**
8 * This describes an API error.
9 *
10 * @method static self modelNotFound()
11 * The request model was not found (typically the customer)
12 *
13 * @method static self validationFailed()
14 * This means some data could not be validated,
15 * you may have to look into message to get the details
16 *
17 * @method static self customerExists()
18 * The customer already exists
19 *
20 * @method static self customerNotActivated()
21 * The customer account is not activated and cannot perform
22 * this action
23 *
24 * @method static self customerAlreadyActive()
25 * You have requested to activate a customer account,
26 * but it is already active
27 *
28 * @method static self singleOptinCustomerExists()
29 * You have requested to create a single optin for an email,
30 * but it already exists
31 *
32 * @method static self apiAuthFailed()
33 * The authentification to the api failed. You may have to
34 * check into config or .env file for your service credentials
35 *
36 * @method static self authFailed()
37 * The authentification for this customer failed because of wrong credentials
38 *
39 * @method static self methodNotAllowed()
40 * This http method is not allowed
41 *
42 * @method static self wrongTimeStamp()
43 * The timestamp given is wrong. This would happen in case if
44 * a replay attack scenario. Timestamps are provided in headers
45 * and in the http body
46 *
47 * @method static self missingParameter()
48 * The request is missing some parameters
49 *
50 * @method static self throttleRequestsException()
51 * Too many requests have been attempted. The is a throttle security.
52 * Normally you should not trigger this.
53 * If this happens fill a ticket on https://supportweb.ldc.fr
54 * with some technical details
55 *
56 * @method static self authorizationException()
57 * You are maybe trying to access data that is out of your range.
58 * This should not happen normally, it is here to allow you to handle this case.
59 * If this happens fill a ticket on https://supportweb.ldc.fr
60 * with some technical details
61 *
62 * @package LDCCrmClient
63 * @subpackage enums
64 */
65class ApiError extends BaseEnum
66{
67    /**
68     * Valeurs des possibilités de cet énumération (value)
69     *
70     * @return array
71     */
72    public static function values(): array
73    {
74        return [
75            'modelNotFound' => 101,
76            'validationFailed' => 102,
77            'customerExists' => 103,
78            'customerAlreadyActive' => 110,
79            'customerNotActivated' => 116,
80            'authFailed' => 117,
81            'apiAuthFailed' => 119,
82            'singleOptinCustomerExists' => 120,
83            'methodNotAllowed' => 150,
84            'wrongTimeStamp' => 151,
85            'missingParameter' => 400,
86            'authorizationException' => 403,
87            'throttleRequestsException' => 429
88        ];
89    }
90
91    /**
92     * Libellés des possibilités de cet énumération (label)
93     *
94     * @return array
95     */
96    public static function labels(): array
97    {
98        return [
99            'modelNotFound' => '%s introuvable.',
100            'validationFailed' => 'Impossible de valider les données',
101            'customerExists' => 'L\'utilisateur est déjà enregistré sur ce site',
102            'customerNotActivated' => 'Le compte n\'est pas activé',
103            'customerAlreadyActive' => 'Le compte est déjà activé',
104            'singleOptinCustomerExists' => 'Le single optin ne peut être ajouté car un compte existe.',
105            'authFailed' => 'Echec de l\'authentification du compte',
106            'apiAuthFailed' => 'Echec de l\'authentification client à l\'Api LDC-CRM pour ce service',
107            'methodNotAllowed' => 'This method is not allowed for this request',
108            'wrongTimeStamp' => 'Your request is malformed, please check timestamp header and parameter',
109            'missingParameter' => 'The request is missing some parameters',
110            'authorizationException' => 'You are not autorized to access this',
111            'throttleRequestsException' => 'Throttle: too many request have been attempted, please retry later'
112        ];
113    }
114}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Enums/ApiStatus.php.html b/tests/coverage/clients/V2/src/Enums/ApiStatus.php.html new file mode 100644 index 0000000..f53418e --- /dev/null +++ b/tests/coverage/clients/V2/src/Enums/ApiStatus.php.html @@ -0,0 +1,120 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Enums/ApiStatus.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ApiStatus
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Enums;
4
5use Kwaadpepper\Enum\BaseEnum;
6
7/**
8 * @method static self OK()
9 * @method static self KO()
10 *
11 * @package LDCCrmClient
12 * @subpackage enums
13 */
14class ApiStatus extends BaseEnum
15{
16}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Enums/ArcadiaMailType.php.html b/tests/coverage/clients/V2/src/Enums/ArcadiaMailType.php.html new file mode 100644 index 0000000..86a660e --- /dev/null +++ b/tests/coverage/clients/V2/src/Enums/ArcadiaMailType.php.html @@ -0,0 +1,183 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Enums/ArcadiaMailType.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ArcadiaMailType
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 values
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Enums;
4
5use Kwaadpepper\Enum\BaseEnum;
6
7/**
8 * @method static self default()
9 * @method static self game()
10 *
11 * @package LDCCrmClient
12 * @subpackage enums
13 */
14class ArcadiaMailType extends BaseEnum
15{
16    /**
17     * Valeurs des possibilités de cet énumération (value)
18     *
19     * @return array
20     */
21    public static function values(): array
22    {
23        return [
24            'default' => 1,
25            'game' => 2
26        ];
27    }
28}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Enums/ArcadiaOrderType.php.html b/tests/coverage/clients/V2/src/Enums/ArcadiaOrderType.php.html new file mode 100644 index 0000000..153845d --- /dev/null +++ b/tests/coverage/clients/V2/src/Enums/ArcadiaOrderType.php.html @@ -0,0 +1,183 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Enums/ArcadiaOrderType.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ArcadiaOrderType
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Enums;
4
5use Kwaadpepper\Enum\BaseEnum;
6
7/**
8 * @method static self cb()
9 * @method static self check()
10 *
11 * @package LDCCrmClient
12 * @subpackage enums
13 */
14class ArcadiaOrderType extends BaseEnum
15{
16    /**
17     * Valeurs des possibilités de cet énumération (value)
18     *
19     * @return array
20     */
21    public static function values(): array
22    {
23        return [
24            'cb' => 0,
25            'check' => 1
26        ];
27    }
28}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Enums/Country.php.html b/tests/coverage/clients/V2/src/Enums/Country.php.html new file mode 100644 index 0000000..74c4755 --- /dev/null +++ b/tests/coverage/clients/V2/src/Enums/Country.php.html @@ -0,0 +1,960 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Enums/Country.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
Country
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 labels
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Enums;
4
5use Kwaadpepper\Enum\BaseEnum;
6
7/**
8 * Liste de Pays
9 * @source https://datahub.io/core/country-list Data Hub country list
10 * @method static self afghanistan()
11 * @method static self alandIslands()
12 * @method static self albania()
13 * @method static self algeria()
14 * @method static self americanSamoa()
15 * @method static self andorra()
16 * @method static self angola()
17 * @method static self anguilla()
18 * @method static self antarctica()
19 * @method static self antiguaAndBarbuda()
20 * @method static self argentina()
21 * @method static self armenia()
22 * @method static self aruba()
23 * @method static self australia()
24 * @method static self austria()
25 * @method static self azerbaijan()
26 * @method static self bahrain()
27 * @method static self bahamas()
28 * @method static self bangladesh()
29 * @method static self barbados()
30 * @method static self belarus()
31 * @method static self belgium()
32 * @method static self belize()
33 * @method static self benin()
34 * @method static self bermuda()
35 * @method static self bhutan()
36 * @method static self boliviaPlurinationalStateOf()
37 * @method static self bonaireSintEustatiusAndSaba()
38 * @method static self bosniaAndHerzegovina()
39 * @method static self botswana()
40 * @method static self bouvetIsland()
41 * @method static self brazil()
42 * @method static self britishIndianOceanTerritory()
43 * @method static self bruneiDarussalam()
44 * @method static self bulgaria()
45 * @method static self burkinaFaso()
46 * @method static self burundi()
47 * @method static self cambodia()
48 * @method static self cameroon()
49 * @method static self canada()
50 * @method static self capeVerde()
51 * @method static self caymanIslands()
52 * @method static self centralAfricanRepublic()
53 * @method static self chad()
54 * @method static self chile()
55 * @method static self china()
56 * @method static self christmasIsland()
57 * @method static self cocosKeelingIslands()
58 * @method static self colombia()
59 * @method static self comoros()
60 * @method static self congo()
61 * @method static self congoTheDemocraticRepublicOfThe()
62 * @method static self cookIslands()
63 * @method static self costaRica()
64 * @method static self coteDivoire()
65 * @method static self croatia()
66 * @method static self cuba()
67 * @method static self curacao()
68 * @method static self cyprus()
69 * @method static self czechRepublic()
70 * @method static self denmark()
71 * @method static self djibouti()
72 * @method static self dominica()
73 * @method static self dominicanRepublic()
74 * @method static self ecuador()
75 * @method static self egypt()
76 * @method static self elSalvador()
77 * @method static self equatorialGuinea()
78 * @method static self eritrea()
79 * @method static self estonia()
80 * @method static self ethiopia()
81 * @method static self falklandIslandsMalvinas()
82 * @method static self faroeIslands()
83 * @method static self fiji()
84 * @method static self finland()
85 * @method static self france()
86 * @method static self frenchGuiana()
87 * @method static self frenchPolynesia()
88 * @method static self frenchSouthernTerritories()
89 * @method static self gabon()
90 * @method static self gambia()
91 * @method static self georgia()
92 * @method static self germany()
93 * @method static self ghana()
94 * @method static self gibraltar()
95 * @method static self greece()
96 * @method static self greenland()
97 * @method static self grenada()
98 * @method static self guadeloupe()
99 * @method static self guam()
100 * @method static self guatemala()
101 * @method static self guernsey()
102 * @method static self guinea()
103 * @method static self guineaBissau()
104 * @method static self guyana()
105 * @method static self haiti()
106 * @method static self heardIslandAndMcdonaldIslands()
107 * @method static self holySeeVaticanCityState()
108 * @method static self honduras()
109 * @method static self hongKong()
110 * @method static self hungary()
111 * @method static self iceland()
112 * @method static self india()
113 * @method static self indonesia()
114 * @method static self iranIslamicRepublicOf()
115 * @method static self iraq()
116 * @method static self ireland()
117 * @method static self isleOfMan()
118 * @method static self israel()
119 * @method static self italy()
120 * @method static self jamaica()
121 * @method static self japan()
122 * @method static self jersey()
123 * @method static self jordan()
124 * @method static self kazakhstan()
125 * @method static self kenya()
126 * @method static self kiribati()
127 * @method static self koreaDemocraticPeoplesRepublicOf()
128 * @method static self koreaRepublicOf()
129 * @method static self kuwait()
130 * @method static self kyrgyzstan()
131 * @method static self laoPeoplesDemocraticRepublic()
132 * @method static self latvia()
133 * @method static self lebanon()
134 * @method static self lesotho()
135 * @method static self liberia()
136 * @method static self libya()
137 * @method static self liechtenstein()
138 * @method static self lithuania()
139 * @method static self luxembourg()
140 * @method static self macao()
141 * @method static self macedoniaTheFormerYugoslavRepublicOf()
142 * @method static self madagascar()
143 * @method static self malawi()
144 * @method static self malaysia()
145 * @method static self maldives()
146 * @method static self mali()
147 * @method static self malta()
148 * @method static self marshallIslands()
149 * @method static self martinique()
150 * @method static self mauritania()
151 * @method static self mauritius()
152 * @method static self mayotte()
153 * @method static self mexico()
154 * @method static self micronesiaFederatedStatesOf()
155 * @method static self moldovaRepublicOf()
156 * @method static self monaco()
157 * @method static self mongolia()
158 * @method static self montenegro()
159 * @method static self montserrat()
160 * @method static self morocco()
161 * @method static self mozambique()
162 * @method static self myanmar()
163 * @method static self namibia()
164 * @method static self nauru()
165 * @method static self nepal()
166 * @method static self netherlands()
167 * @method static self newCaledonia()
168 * @method static self newZealand()
169 * @method static self nicaragua()
170 * @method static self niger()
171 * @method static self nigeria()
172 * @method static self niue()
173 * @method static self norfolkIsland()
174 * @method static self northernMarianaIslands()
175 * @method static self norway()
176 * @method static self oman()
177 * @method static self pakistan()
178 * @method static self palau()
179 * @method static self palestineStateOf()
180 * @method static self panama()
181 * @method static self papuaNewGuinea()
182 * @method static self paraguay()
183 * @method static self peru()
184 * @method static self philippines()
185 * @method static self pitcairn()
186 * @method static self poland()
187 * @method static self portugal()
188 * @method static self puertoRico()
189 * @method static self qatar()
190 * @method static self reunion()
191 * @method static self romania()
192 * @method static self russianFederation()
193 * @method static self rwanda()
194 * @method static self saintBarthelemy()
195 * @method static self saintHelenaAscensionAndTristanDaCunha()
196 * @method static self saintKittsAndNevis()
197 * @method static self saintLucia()
198 * @method static self saintMartinFrenchPart()
199 * @method static self saintPierreAndMiquelon()
200 * @method static self saintVincentAndTheGrenadines()
201 * @method static self samoa()
202 * @method static self sanMarino()
203 * @method static self saoTomeAndPrincipe()
204 * @method static self saudiArabia()
205 * @method static self senegal()
206 * @method static self serbia()
207 * @method static self seychelles()
208 * @method static self sierraLeone()
209 * @method static self singapore()
210 * @method static self sintMaartenDutchPart()
211 * @method static self slovakia()
212 * @method static self slovenia()
213 * @method static self solomonIslands()
214 * @method static self somalia()
215 * @method static self southAfrica()
216 * @method static self southGeorgiaAndTheSouthSandwichIslands()
217 * @method static self southSudan()
218 * @method static self spain()
219 * @method static self sriLanka()
220 * @method static self sudan()
221 * @method static self suriname()
222 * @method static self svalbardAndJanMayen()
223 * @method static self swaziland()
224 * @method static self sweden()
225 * @method static self switzerland()
226 * @method static self syrianArabRepublic()
227 * @method static self taiwanProvinceOfChina()
228 * @method static self tajikistan()
229 * @method static self tanzaniaUnitedRepublicOf()
230 * @method static self thailand()
231 * @method static self timorLeste()
232 * @method static self togo()
233 * @method static self tokelau()
234 * @method static self tonga()
235 * @method static self trinidadAndTobago()
236 * @method static self tunisia()
237 * @method static self turkey()
238 * @method static self turkmenistan()
239 * @method static self turksAndCaicosIslands()
240 * @method static self tuvalu()
241 * @method static self uganda()
242 * @method static self ukraine()
243 * @method static self unitedArabEmirates()
244 * @method static self unitedKingdom()
245 * @method static self unitedStates()
246 * @method static self unitedStatesMinorOutlyingIslands()
247 * @method static self uruguay()
248 * @method static self uzbekistan()
249 * @method static self vanuatu()
250 * @method static self venezuelaBolivarianRepublicOf()
251 * @method static self vietNam()
252 * @method static self virginIslandsBritish()
253 * @method static self virginIslandsUs()
254 * @method static self wallisAndFutuna()
255 * @method static self westernSahara()
256 * @method static self yemen()
257 * @method static self zambia()
258 * @method static self zimbabwe()
259 *
260 * @package LDCCrmClient
261 * @subpackage enums
262 */
263class Country extends BaseEnum
264{
265    /**
266     * Valeurs des possibilités de cet énumération (value)
267     *
268     * @return array
269     */
270    public static function values(): array
271    {
272        return [
273            'afghanistan' => 1,
274            'alandIslands' => 2,
275            'albania' => 3,
276            'algeria' => 4,
277            'americanSamoa' => 5,
278            'andorra' => 6,
279            'angola' => 7,
280            'anguilla' => 8,
281            'antarctica' => 9,
282            'antiguaAndBarbuda' => 10,
283            'argentina' => 11,
284            'armenia' => 12,
285            'aruba' => 13,
286            'australia' => 14,
287            'austria' => 15,
288            'azerbaijan' => 16,
289            'bahrain' => 17,
290            'bahamas' => 18,
291            'bangladesh' => 19,
292            'barbados' => 20,
293            'belarus' => 21,
294            'belgium' => 22,
295            'belize' => 23,
296            'benin' => 24,
297            'bermuda' => 25,
298            'bhutan' => 26,
299            'boliviaPlurinationalStateOf' => 27,
300            'bonaireSintEustatiusAndSaba' => 28,
301            'bosniaAndHerzegovina' => 29,
302            'botswana' => 30,
303            'bouvetIsland' => 31,
304            'brazil' => 32,
305            'britishIndianOceanTerritory' => 33,
306            'bruneiDarussalam' => 34,
307            'bulgaria' => 35,
308            'burkinaFaso' => 36,
309            'burundi' => 37,
310            'cambodia' => 38,
311            'cameroon' => 39,
312            'canada' => 40,
313            'capeVerde' => 41,
314            'caymanIslands' => 42,
315            'centralAfricanRepublic' => 43,
316            'chad' => 44,
317            'chile' => 45,
318            'china' => 46,
319            'christmasIsland' => 47,
320            'cocosKeelingIslands' => 48,
321            'colombia' => 49,
322            'comoros' => 50,
323            'congo' => 51,
324            'congoTheDemocraticRepublicOfThe' => 52,
325            'cookIslands' => 53,
326            'costaRica' => 54,
327            'coteDivoire' => 55,
328            'croatia' => 56,
329            'cuba' => 57,
330            'curacao' => 58,
331            'cyprus' => 59,
332            'czechRepublic' => 60,
333            'denmark' => 61,
334            'djibouti' => 62,
335            'dominica' => 63,
336            'dominicanRepublic' => 64,
337            'ecuador' => 65,
338            'egypt' => 66,
339            'elSalvador' => 67,
340            'equatorialGuinea' => 68,
341            'eritrea' => 69,
342            'estonia' => 70,
343            'ethiopia' => 71,
344            'falklandIslandsMalvinas' => 72,
345            'faroeIslands' => 73,
346            'fiji' => 74,
347            'finland' => 75,
348            'france' => 76,
349            'frenchGuiana' => 77,
350            'frenchPolynesia' => 78,
351            'frenchSouthernTerritories' => 79,
352            'gabon' => 80,
353            'gambia' => 81,
354            'georgia' => 82,
355            'germany' => 83,
356            'ghana' => 84,
357            'gibraltar' => 85,
358            'greece' => 86,
359            'greenland' => 87,
360            'grenada' => 88,
361            'guadeloupe' => 89,
362            'guam' => 90,
363            'guatemala' => 91,
364            'guernsey' => 92,
365            'guinea' => 93,
366            'guineaBissau' => 94,
367            'guyana' => 95,
368            'haiti' => 96,
369            'heardIslandAndMcdonaldIslands' => 97,
370            'holySeeVaticanCityState' => 98,
371            'honduras' => 99,
372            'hongKong' => 100,
373            'hungary' => 101,
374            'iceland' => 102,
375            'india' => 103,
376            'indonesia' => 104,
377            'iranIslamicRepublicOf' => 105,
378            'iraq' => 106,
379            'ireland' => 107,
380            'isleOfMan' => 108,
381            'israel' => 109,
382            'italy' => 110,
383            'jamaica' => 111,
384            'japan' => 112,
385            'jersey' => 113,
386            'jordan' => 114,
387            'kazakhstan' => 115,
388            'kenya' => 116,
389            'kiribati' => 117,
390            'koreaDemocraticPeoplesRepublicOf' => 118,
391            'koreaRepublicOf' => 119,
392            'kuwait' => 120,
393            'kyrgyzstan' => 121,
394            'laoPeoplesDemocraticRepublic' => 122,
395            'latvia' => 123,
396            'lebanon' => 124,
397            'lesotho' => 125,
398            'liberia' => 126,
399            'libya' => 127,
400            'liechtenstein' => 128,
401            'lithuania' => 129,
402            'luxembourg' => 130,
403            'macao' => 131,
404            'macedoniaTheFormerYugoslavRepublicOf' => 132,
405            'madagascar' => 133,
406            'malawi' => 134,
407            'malaysia' => 135,
408            'maldives' => 136,
409            'mali' => 137,
410            'malta' => 138,
411            'marshallIslands' => 139,
412            'martinique' => 140,
413            'mauritania' => 141,
414            'mauritius' => 142,
415            'mayotte' => 143,
416            'mexico' => 144,
417            'micronesiaFederatedStatesOf' => 145,
418            'moldovaRepublicOf' => 146,
419            'monaco' => 147,
420            'mongolia' => 148,
421            'montenegro' => 149,
422            'montserrat' => 150,
423            'morocco' => 151,
424            'mozambique' => 152,
425            'myanmar' => 153,
426            'namibia' => 154,
427            'nauru' => 155,
428            'nepal' => 156,
429            'netherlands' => 157,
430            'newCaledonia' => 158,
431            'newZealand' => 159,
432            'nicaragua' => 160,
433            'niger' => 161,
434            'nigeria' => 162,
435            'niue' => 163,
436            'norfolkIsland' => 164,
437            'northernMarianaIslands' => 165,
438            'norway' => 166,
439            'oman' => 167,
440            'pakistan' => 168,
441            'palau' => 169,
442            'palestineStateOf' => 170,
443            'panama' => 171,
444            'papuaNewGuinea' => 172,
445            'paraguay' => 173,
446            'peru' => 174,
447            'philippines' => 175,
448            'pitcairn' => 176,
449            'poland' => 177,
450            'portugal' => 178,
451            'puertoRico' => 179,
452            'qatar' => 180,
453            'reunion' => 181,
454            'romania' => 182,
455            'russianFederation' => 183,
456            'rwanda' => 184,
457            'saintBarthelemy' => 185,
458            'saintHelenaAscensionAndTristanDaCunha' => 186,
459            'saintKittsAndNevis' => 187,
460            'saintLucia' => 188,
461            'saintMartinFrenchPart' => 189,
462            'saintPierreAndMiquelon' => 190,
463            'saintVincentAndTheGrenadines' => 191,
464            'samoa' => 192,
465            'sanMarino' => 193,
466            'saoTomeAndPrincipe' => 194,
467            'saudiArabia' => 195,
468            'senegal' => 196,
469            'serbia' => 197,
470            'seychelles' => 198,
471            'sierraLeone' => 199,
472            'singapore' => 200,
473            'sintMaartenDutchPart' => 201,
474            'slovakia' => 202,
475            'slovenia' => 203,
476            'solomonIslands' => 204,
477            'somalia' => 205,
478            'southAfrica' => 206,
479            'southGeorgiaAndTheSouthSandwichIslands' => 207,
480            'southSudan' => 208,
481            'spain' => 209,
482            'sriLanka' => 210,
483            'sudan' => 211,
484            'suriname' => 212,
485            'svalbardAndJanMayen' => 213,
486            'swaziland' => 214,
487            'sweden' => 215,
488            'switzerland' => 216,
489            'syrianArabRepublic' => 217,
490            'taiwanProvinceOfChina' => 218,
491            'tajikistan' => 219,
492            'tanzaniaUnitedRepublicOf' => 220,
493            'thailand' => 221,
494            'timorLeste' => 222,
495            'togo' => 223,
496            'tokelau' => 224,
497            'tonga' => 225,
498            'trinidadAndTobago' => 226,
499            'tunisia' => 227,
500            'turkey' => 228,
501            'turkmenistan' => 229,
502            'turksAndCaicosIslands' => 230,
503            'tuvalu' => 231,
504            'uganda' => 232,
505            'ukraine' => 233,
506            'unitedArabEmirates' => 234,
507            'unitedKingdom' => 235,
508            'unitedStates' => 236,
509            'unitedStatesMinorOutlyingIslands' => 237,
510            'uruguay' => 238,
511            'uzbekistan' => 239,
512            'vanuatu' => 240,
513            'venezuelaBolivarianRepublicOf' => 241,
514            'vietNam' => 242,
515            'virginIslandsBritish' => 243,
516            'virginIslandsUs' => 244,
517            'wallisAndFutuna' => 245,
518            'westernSahara' => 246,
519            'yemen' => 247,
520            'zambia' => 248,
521            'zimbabwe' => 249
522        ];
523    }
524
525    /**
526     * Libellés des possibilités de cet énumération (label)
527     *
528     * @return array
529     */
530    public static function labels(): array
531    {
532        return [
533            'afghanistan' => 'Afghanistan',
534            'alandIslands' => 'Åland Islands',
535            'albania' => 'Albania',
536            'algeria' => 'Algeria',
537            'americanSamoa' => 'American Samoa',
538            'andorra' => 'Andorra',
539            'angola' => 'Angola',
540            'anguilla' => 'Anguilla',
541            'antarctica' => 'Antarctica',
542            'antiguaAndBarbuda' => 'Antigua and Barbuda',
543            'argentina' => 'Argentina',
544            'armenia' => 'Armenia',
545            'aruba' => 'Aruba',
546            'australia' => 'Australia',
547            'austria' => 'Austria',
548            'azerbaijan' => 'Azerbaijan',
549            'bahrain' => 'Bahrain',
550            'bahamas' => 'Bahamas',
551            'bangladesh' => 'Bangladesh',
552            'barbados' => 'Barbados',
553            'belarus' => 'Belarus',
554            'belgium' => 'Belgium',
555            'belize' => 'Belize',
556            'benin' => 'Benin',
557            'bermuda' => 'Bermuda',
558            'bhutan' => 'Bhutan',
559            'boliviaPlurinationalStateOf' => 'Bolivia, Plurinational State of',
560            'bonaireSintEustatiusAndSaba' => 'Bonaire, Sint Eustatius and Saba',
561            'bosniaAndHerzegovina' => 'Bosnia and Herzegovina',
562            'botswana' => 'Botswana',
563            'bouvetIsland' => 'Bouvet Island',
564            'brazil' => 'Brazil',
565            'britishIndianOceanTerritory' => 'British Indian Ocean Territory',
566            'bruneiDarussalam' => 'Brunei Darussalam',
567            'bulgaria' => 'Bulgaria',
568            'burkinaFaso' => 'Burkina Faso',
569            'burundi' => 'Burundi',
570            'cambodia' => 'Cambodia',
571            'cameroon' => 'Cameroon',
572            'canada' => 'Canada',
573            'capeVerde' => 'Cape Verde',
574            'caymanIslands' => 'Cayman Islands',
575            'centralAfricanRepublic' => 'Central African Republic',
576            'chad' => 'Chad',
577            'chile' => 'Chile',
578            'china' => 'China',
579            'christmasIsland' => 'Christmas Island',
580            'cocosKeelingIslands' => 'Cocos (Keeling) Islands',
581            'colombia' => 'Colombia',
582            'comoros' => 'Comoros',
583            'congo' => 'Congo',
584            'congoTheDemocraticRepublicOfThe' => 'Congo, the Democratic Republic of the',
585            'cookIslands' => 'Cook Islands',
586            'costaRica' => 'Costa Rica',
587            'coteDivoire' => 'Côte d\'Ivoire',
588            'croatia' => 'Croatia',
589            'cuba' => 'Cuba',
590            'curacao' => 'Curaçao',
591            'cyprus' => 'Cyprus',
592            'czechRepublic' => 'Czech Republic',
593            'denmark' => 'Denmark',
594            'djibouti' => 'Djibouti',
595            'dominica' => 'Dominica',
596            'dominicanRepublic' => 'Dominican Republic',
597            'ecuador' => 'Ecuador',
598            'egypt' => 'Egypt',
599            'elSalvador' => 'El Salvador',
600            'equatorialGuinea' => 'Equatorial Guinea',
601            'eritrea' => 'Eritrea',
602            'estonia' => 'Estonia',
603            'ethiopia' => 'Ethiopia',
604            'falklandIslandsMalvinas' => 'Falkland Islands (Malvinas)',
605            'faroeIslands' => 'Faroe Islands',
606            'fiji' => 'Fiji',
607            'finland' => 'Finland',
608            'france' => 'France',
609            'frenchGuiana' => 'French Guiana',
610            'frenchPolynesia' => 'French Polynesia',
611            'frenchSouthernTerritories' => 'French Southern Territories',
612            'gabon' => 'Gabon',
613            'gambia' => 'Gambia',
614            'georgia' => 'Georgia',
615            'germany' => 'Germany',
616            'ghana' => 'Ghana',
617            'gibraltar' => 'Gibraltar',
618            'greece' => 'Greece',
619            'greenland' => 'Greenland',
620            'grenada' => 'Grenada',
621            'guadeloupe' => 'Guadeloupe',
622            'guam' => 'Guam',
623            'guatemala' => 'Guatemala',
624            'guernsey' => 'Guernsey',
625            'guinea' => 'Guinea',
626            'guineaBissau' => 'GuineaBissau',
627            'guyana' => 'Guyana',
628            'haiti' => 'Haiti',
629            'heardIslandAndMcdonaldIslands' => 'Heard Island and McDonald Islands',
630            'holySeeVaticanCityState' => 'Holy See (Vatican City State)',
631            'honduras' => 'Honduras',
632            'hongKong' => 'Hong Kong',
633            'hungary' => 'Hungary',
634            'iceland' => 'Iceland',
635            'india' => 'India',
636            'indonesia' => 'Indonesia',
637            'iranIslamicRepublicOf' => 'Iran, Islamic Republic of',
638            'iraq' => 'Iraq',
639            'ireland' => 'Ireland',
640            'isleOfMan' => 'Isle of Man',
641            'israel' => 'Israel',
642            'italy' => 'Italy',
643            'jamaica' => 'Jamaica',
644            'japan' => 'Japan',
645            'jersey' => 'Jersey',
646            'jordan' => 'Jordan',
647            'kazakhstan' => 'Kazakhstan',
648            'kenya' => 'Kenya',
649            'kiribati' => 'Kiribati',
650            'koreaDemocraticPeoplesRepublicOf' => 'Korea, Democratic People\'s Republic of',
651            'koreaRepublicOf' => 'Korea, Republic of',
652            'kuwait' => 'Kuwait',
653            'kyrgyzstan' => 'Kyrgyzstan',
654            'laoPeoplesDemocraticRepublic' => 'Lao People\'s Democratic Republic',
655            'latvia' => 'Latvia',
656            'lebanon' => 'Lebanon',
657            'lesotho' => 'Lesotho',
658            'liberia' => 'Liberia',
659            'libya' => 'Libya',
660            'liechtenstein' => 'Liechtenstein',
661            'lithuania' => 'Lithuania',
662            'luxembourg' => 'Luxembourg',
663            'macao' => 'Macao',
664            'macedoniaTheFormerYugoslavRepublicOf' => 'Macedonia, the Former Yugoslav Republic of',
665            'madagascar' => 'Madagascar',
666            'malawi' => 'Malawi',
667            'malaysia' => 'Malaysia',
668            'maldives' => 'Maldives',
669            'mali' => 'Mali',
670            'malta' => 'Malta',
671            'marshallIslands' => 'Marshall Islands',
672            'martinique' => 'Martinique',
673            'mauritania' => 'Mauritania',
674            'mauritius' => 'Mauritius',
675            'mayotte' => 'Mayotte',
676            'mexico' => 'Mexico',
677            'micronesiaFederatedStatesOf' => 'Micronesia, Federated States of',
678            'moldovaRepublicOf' => 'Moldova, Republic of',
679            'monaco' => 'Monaco',
680            'mongolia' => 'Mongolia',
681            'montenegro' => 'Montenegro',
682            'montserrat' => 'Montserrat',
683            'morocco' => 'Morocco',
684            'mozambique' => 'Mozambique',
685            'myanmar' => 'Myanmar',
686            'namibia' => 'Namibia',
687            'nauru' => 'Nauru',
688            'nepal' => 'Nepal',
689            'netherlands' => 'Netherlands',
690            'newCaledonia' => 'New Caledonia',
691            'newZealand' => 'New Zealand',
692            'nicaragua' => 'Nicaragua',
693            'niger' => 'Niger',
694            'nigeria' => 'Nigeria',
695            'niue' => 'Niue',
696            'norfolkIsland' => 'Norfolk Island',
697            'northernMarianaIslands' => 'Northern Mariana Islands',
698            'norway' => 'Norway',
699            'oman' => 'Oman',
700            'pakistan' => 'Pakistan',
701            'palau' => 'Palau',
702            'palestineStateOf' => 'Palestine, State of',
703            'panama' => 'Panama',
704            'papuaNewGuinea' => 'Papua New Guinea',
705            'paraguay' => 'Paraguay',
706            'peru' => 'Peru',
707            'philippines' => 'Philippines',
708            'pitcairn' => 'Pitcairn',
709            'poland' => 'Poland',
710            'portugal' => 'Portugal',
711            'puertoRico' => 'Puerto Rico',
712            'qatar' => 'Qatar',
713            'reunion' => 'Réunion',
714            'romania' => 'Romania',
715            'russianFederation' => 'Russian Federation',
716            'rwanda' => 'Rwanda',
717            'saintBarthelemy' => 'Saint Barthélemy',
718            'saintHelenaAscensionAndTristanDaCunha' => 'Saint Helena, Ascension and Tristan da Cunha',
719            'saintKittsAndNevis' => 'Saint Kitts and Nevis',
720            'saintLucia' => 'Saint Lucia',
721            'saintMartinFrenchPart' => 'Saint Martin (French part)',
722            'saintPierreAndMiquelon' => 'Saint Pierre and Miquelon',
723            'saintVincentAndTheGrenadines' => 'Saint Vincent and the Grenadines',
724            'samoa' => 'Samoa',
725            'sanMarino' => 'San Marino',
726            'saoTomeAndPrincipe' => 'Sao Tome and Principe',
727            'saudiArabia' => 'Saudi Arabia',
728            'senegal' => 'Senegal',
729            'serbia' => 'Serbia',
730            'seychelles' => 'Seychelles',
731            'sierraLeone' => 'Sierra Leone',
732            'singapore' => 'Singapore',
733            'sintMaartenDutchPart' => 'Sint Maarten (Dutch part)',
734            'slovakia' => 'Slovakia',
735            'slovenia' => 'Slovenia',
736            'solomonIslands' => 'Solomon Islands',
737            'somalia' => 'Somalia',
738            'southAfrica' => 'South Africa',
739            'southGeorgiaAndTheSouthSandwichIslands' => 'South Georgia and the South Sandwich Islands',
740            'southSudan' => 'South Sudan',
741            'spain' => 'Spain',
742            'sriLanka' => 'Sri Lanka',
743            'sudan' => 'Sudan',
744            'suriname' => 'Suriname',
745            'svalbardAndJanMayen' => 'Svalbard and Jan Mayen',
746            'swaziland' => 'Swaziland',
747            'sweden' => 'Sweden',
748            'switzerland' => 'Switzerland',
749            'syrianArabRepublic' => 'Syrian Arab Republic',
750            'taiwanProvinceOfChina' => 'Taiwan, Province of China',
751            'tajikistan' => 'Tajikistan',
752            'tanzaniaUnitedRepublicOf' => 'Tanzania, United Republic of',
753            'thailand' => 'Thailand',
754            'timorLeste' => 'TimorLeste',
755            'togo' => 'Togo',
756            'tokelau' => 'Tokelau',
757            'tonga' => 'Tonga',
758            'trinidadAndTobago' => 'Trinidad and Tobago',
759            'tunisia' => 'Tunisia',
760            'turkey' => 'Turkey',
761            'turkmenistan' => 'Turkmenistan',
762            'turksAndCaicosIslands' => 'Turks and Caicos Islands',
763            'tuvalu' => 'Tuvalu',
764            'uganda' => 'Uganda',
765            'ukraine' => 'Ukraine',
766            'unitedArabEmirates' => 'United Arab Emirates',
767            'unitedKingdom' => 'United Kingdom',
768            'unitedStates' => 'United States',
769            'unitedStatesMinorOutlyingIslands' => 'United States Minor Outlying Islands',
770            'uruguay' => 'Uruguay',
771            'uzbekistan' => 'Uzbekistan',
772            'vanuatu' => 'Vanuatu',
773            'venezuelaBolivarianRepublicOf' => 'Venezuela, Bolivarian Republic of',
774            'vietNam' => 'Viet Nam',
775            'virginIslandsBritish' => 'Virgin Islands, British',
776            'virginIslandsUs' => 'Virgin Islands, U.S.',
777            'wallisAndFutuna' => 'Wallis and Futuna',
778            'westernSahara' => 'Western Sahara',
779            'yemen' => 'Yemen',
780            'zambia' => 'Zambia',
781            'zimbabwe' => 'Zimbabwe'
782        ];
783    }
784}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Enums/HttpMethods.php.html b/tests/coverage/clients/V2/src/Enums/HttpMethods.php.html new file mode 100644 index 0000000..222022a --- /dev/null +++ b/tests/coverage/clients/V2/src/Enums/HttpMethods.php.html @@ -0,0 +1,120 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Enums/HttpMethods.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
HttpMethods
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Enums;
4
5use Kwaadpepper\Enum\BaseEnum;
6
7/**
8 * @ignore This is used on internal
9 * @method static self get()
10 * @method static self post()
11 * @method static self put()
12 * @method static self delete()
13 */
14class HttpMethods extends BaseEnum
15{
16}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Enums/LogLevel.php.html b/tests/coverage/clients/V2/src/Enums/LogLevel.php.html new file mode 100644 index 0000000..36ae39d --- /dev/null +++ b/tests/coverage/clients/V2/src/Enums/LogLevel.php.html @@ -0,0 +1,195 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Enums/LogLevel.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
LogLevel
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 values
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Enums;
4
5use Kwaadpepper\Enum\BaseEnum;
6
7/**
8 * @method static self EMERG() system is unusable
9 * @method static self ALERT() action must be taken immediately
10 * @method static self CRIT() critical conditions
11 * @method static self ERR() critical conditions
12 * @method static self WARNING() critical conditions
13 * @method static self NOTICE() normal, but significant, condition
14 * @method static self INFO() informational message
15 * @method static self DEBUG() debug-level message
16 *
17 * @package LDCCrmClient
18 * @subpackage enums
19 */
20class LogLevel extends BaseEnum
21{
22    /**
23     * Valeurs des possibilités de cet énumération (value)
24     *
25     * @return array
26     */
27    protected static function values()
28    {
29        return [
30            'EMERG' => 'LOG_EMERG',
31            'ALERT' => 'LOG_ALERT',
32            'CRIT' => 'LOG_CRIT',
33            'ERR' => 'LOG_ERR',
34            'WARNING' => 'LOG_WARNING',
35            'NOTICE' => 'LOG_NOTICE',
36            'INFO' => 'LOG_INFO',
37            'DEBUG' => 'LOG_DEBUG',
38        ];
39    }
40}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Enums/QuestionType.php.html b/tests/coverage/clients/V2/src/Enums/QuestionType.php.html new file mode 100644 index 0000000..d4f20b2 --- /dev/null +++ b/tests/coverage/clients/V2/src/Enums/QuestionType.php.html @@ -0,0 +1,185 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Enums/QuestionType.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
QuestionType
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 values
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Enums;
4
5use Kwaadpepper\Enum\BaseEnum;
6
7/**
8 * @method static self select()
9 * @method static self radio()
10 * @method static self checkbox()
11 *
12 * @package LDCCrmClient
13 * @subpackage enums
14 */
15class QuestionType extends BaseEnum
16{
17    /**
18     * Valeurs des possibilités de cet énumération (value)
19     *
20     * @return array
21     */
22    public static function values(): array
23    {
24        return [
25            'select' => 1,
26            'radio' => 2,
27            'checkbox' => 3
28        ];
29    }
30}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Enums/dashboard.html b/tests/coverage/clients/V2/src/Enums/dashboard.html new file mode 100644 index 0000000..dad1a6c --- /dev/null +++ b/tests/coverage/clients/V2/src/Enums/dashboard.html @@ -0,0 +1,291 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/clients/V2/src/Enums + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
ClassCoverage
LDC\LDCCRM\Enums\ArcadiaMailType0%
LDC\LDCCRM\Enums\LogLevel0%
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
ClassCRAP
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
MethodCoverage
values0%
values0%
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Enums/index.html b/tests/coverage/clients/V2/src/Enums/index.html new file mode 100644 index 0000000..1c4882a --- /dev/null +++ b/tests/coverage/clients/V2/src/Enums/index.html @@ -0,0 +1,288 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Enums + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
+
+ 66.67% covered (warning) +
+
+
66.67%
4 / 6
ApiError.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiStatus.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ArcadiaMailType.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ArcadiaOrderType.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Country.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
HttpMethods.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
LogLevel.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
QuestionType.php
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/clients/V2/src/Exceptions/AccessError.php.html b/tests/coverage/clients/V2/src/Exceptions/AccessError.php.html new file mode 100644 index 0000000..a94a93a --- /dev/null +++ b/tests/coverage/clients/V2/src/Exceptions/AccessError.php.html @@ -0,0 +1,180 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Exceptions/AccessError.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
AccessError
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Exceptions;
4
5use Error;
6
7/**
8 * This is thrown when trying to set a value on an object property that does not exist.
9 *
10 * @package LDCCrmClient
11 * @subpackage exceptions
12 */
13class AccessError extends Error
14{
15    /**
16     * AccessError
17     *
18     * @param string $objectName   The object name where the property does not exist.
19     * @param string $propertyName The property name that does not exist.
20     */
21    public function __construct(string $objectName, string $propertyName)
22    {
23        parent::__construct("Tried to set value in $objectName->$propertyName, it does not exists.");
24    }
25}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Exceptions/ApiErrorException.php.html b/tests/coverage/clients/V2/src/Exceptions/ApiErrorException.php.html new file mode 100644 index 0000000..83b80f0 --- /dev/null +++ b/tests/coverage/clients/V2/src/Exceptions/ApiErrorException.php.html @@ -0,0 +1,322 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Exceptions/ApiErrorException.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
CRAP
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
ApiErrorException
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
5.20
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 getApiError
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 getStatusCode
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 report
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Exceptions;
4
5use LDC\LDCCRM\Enums\ApiError;
6use Exception;
7use Illuminate\Support\Facades\Log;
8use Throwable;
9
10/**
11 * This would be thrown if any of the case
12 * referenced in \LDC\LDCCRM\Enums\ApiError would
13 * occur within the crm
14 *
15 * On the client an ApiErrorException would
16 * also be throw with the ApiError corresponding to
17 * the situation
18 *
19 * @package LDCCrmClient
20 * @subpackage exceptions
21 */
22class ApiErrorException extends Exception
23{
24    /**
25     * Api Error
26     * This value makes the error more explicit.
27     * Handling these errors are normal as inputs or
28     * request may be undesired or refused by the crm.
29     *
30     * @var \LDC\LDCCRM\Enums\ApiError
31     */
32    protected $error;
33
34    /**
35     * This is an explicit error message,
36     * ou may print this to the front or not depending
37     * on $error value
38     *
39     * @var string
40     */
41    protected $message;
42
43    /**
44     * This is the http code returned by the crm.
45     * This value could be used to handle errors.
46     * But please take in consideration $error first
47     *
48     * @var integer
49     */
50    protected $httpCode;
51
52    /**
53     * Create a new exception base on ApiError enum.
54     *
55     * @param ApiError       $error
56     * @param integer        $httpCode
57     * @param string         $message
58     * @param Throwable|null $previous
59     */
60    public function __construct(ApiError $error, int $httpCode = 400, string $message = '', Throwable $previous = null)
61    {
62        $this->error    = $error;
63        $this->message  = $message;
64        $this->httpCode = $httpCode;
65        parent::__construct(
66            strlen($this->message) ? $this->message : $error->label,
67            $error->value,
68            $previous
69        );
70    }
71
72    /**
73     * Get the Api Error
74     *
75     * @return ApiError
76     */
77    public function getApiError(): ApiError
78    {
79        return $this->error;
80    }
81
82    /**
83     * Get the http code
84     *
85     * @return integer
86     */
87    public function getStatusCode(): int
88    {
89        return $this->httpCode;
90    }
91
92    /**
93     * This is used by laravel only.
94     * It uses Log class to print the error.
95     *
96     * @return void
97     */
98    public function report()
99    {
100        // @codeCoverageIgnoreStart
101        Log::debug(sprintf('CRM API: %s', $this->getMessage()));
102        // @codeCoverageIgnoreEnd
103    }
104}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Exceptions/CrmException.php.html b/tests/coverage/clients/V2/src/Exceptions/CrmException.php.html new file mode 100644 index 0000000..6d18264 --- /dev/null +++ b/tests/coverage/clients/V2/src/Exceptions/CrmException.php.html @@ -0,0 +1,144 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Exceptions/CrmException.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
CrmException
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 report
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Exceptions;
4
5use Exception;
6use Illuminate\Support\Facades\Log;
7
8/**
9 * This is a critical error.
10 * The request could not be handled. You may dump any value you can
11 * get before submitting a ticker to https://supportweb.ldc.fr
12 *
13 * @package LDCCrmClient
14 * @subpackage exceptions
15 * @codeCoverageIgnore
16 */
17class CrmException extends Exception
18{
19    /**
20     * This is used by laravel only.
21     * It uses Log class to print the error.
22     *
23     * @return void
24     */
25    public function report()
26    {
27        Log::critical(sprintf('CRM API: %s', $this->getMessage()));
28    }
29}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Exceptions/SkipValidationException.php.html b/tests/coverage/clients/V2/src/Exceptions/SkipValidationException.php.html new file mode 100644 index 0000000..a189115 --- /dev/null +++ b/tests/coverage/clients/V2/src/Exceptions/SkipValidationException.php.html @@ -0,0 +1,119 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Exceptions/SkipValidationException.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
SkipValidationException
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Exceptions;
4
5use Exception;
6
7/**
8 * Used only for internal validator
9 *
10 * @package LDCCrmClient
11 * @subpackage exceptions
12 */
13class SkipValidationException extends Exception
14{
15}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Exceptions/ValidationException.php.html b/tests/coverage/clients/V2/src/Exceptions/ValidationException.php.html new file mode 100644 index 0000000..0c45ee7 --- /dev/null +++ b/tests/coverage/clients/V2/src/Exceptions/ValidationException.php.html @@ -0,0 +1,143 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Exceptions/ValidationException.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ValidationException
n/a
0 / 0
n/a
0 / 0
1
n/a
0 / 0
 report
n/a
0 / 0
1
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Exceptions;
4
5use Exception;
6use Illuminate\Support\Facades\Log;
7
8/**
9 * This would be thrown if a data could not be validated
10 * on any models
11 *
12 * @package LDCCrmClient
13 * @subpackage exceptions
14 * @codeCoverageIgnore
15 */
16class ValidationException extends Exception
17{
18    /**
19     * This is used by laravel only.
20     * It uses Log class to print the error.
21     *
22     * @return void
23     */
24    public function report()
25    {
26        Log::error(sprintf('CRM Client: %s', $this->getMessage()));
27    }
28}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Exceptions/WpCrmInvalidJsonUserInfo.php.html b/tests/coverage/clients/V2/src/Exceptions/WpCrmInvalidJsonUserInfo.php.html new file mode 100644 index 0000000..d24c856 --- /dev/null +++ b/tests/coverage/clients/V2/src/Exceptions/WpCrmInvalidJsonUserInfo.php.html @@ -0,0 +1,182 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Exceptions/WpCrmInvalidJsonUserInfo.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
WpCrmInvalidJsonUserInfo
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
 __construct
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Exceptions;
4
5use Error;
6use LDC\LDCCRM\Enums\LogLevel;
7use LDC\LDCCRM\Utils;
8
9/**
10 * This is thrown when trying to read WP customer from DB json data.
11 *
12 * @package LDCCrmClient
13 * @subpackage exceptions
14 */
15class WpCrmInvalidJsonUserInfo extends Error
16{
17    /**
18     * WpCrmInvalidJsonUserInfo
19     *
20     * @param string $message The error message.
21     */
22    public function __construct(string $message)
23    {
24        Utils::log($message, LogLevel::CRIT());
25        parent::__construct($message);
26    }
27}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Exceptions/dashboard.html b/tests/coverage/clients/V2/src/Exceptions/dashboard.html new file mode 100644 index 0000000..3ab6621 --- /dev/null +++ b/tests/coverage/clients/V2/src/Exceptions/dashboard.html @@ -0,0 +1,293 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/clients/V2/src/Exceptions + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+
+

Project Risks

+
+ + + + + + + + + + + +
ClassCRAP
LDC\LDCCRM\Exceptions\ApiErrorException5
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + +
MethodCoverage
getStatusCode0%
report0%
__construct0%
+
+
+
+

Project Risks

+
+ + + + + + + + + + +
MethodCRAP
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Exceptions/index.html b/tests/coverage/clients/V2/src/Exceptions/index.html new file mode 100644 index 0000000..17bc3dd --- /dev/null +++ b/tests/coverage/clients/V2/src/Exceptions/index.html @@ -0,0 +1,217 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Exceptions + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 66.67% covered (warning) +
+
+
66.67%
10 / 15
+
+ 50.00% covered (danger) +
+
+
50.00%
3 / 6
+
+ 33.33% covered (danger) +
+
+
33.33%
1 / 3
AccessError.php
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ApiErrorException.php
+
+ 80.00% covered (warning) +
+
+
80.00%
8 / 10
+
+ 50.00% covered (danger) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CrmException.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
SkipValidationException.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ValidationException.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
WpCrmInvalidJsonUserInfo.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/tests/coverage/clients/V2/src/Models/Answer.php.html b/tests/coverage/clients/V2/src/Models/Answer.php.html new file mode 100644 index 0000000..d239a16 --- /dev/null +++ b/tests/coverage/clients/V2/src/Models/Answer.php.html @@ -0,0 +1,331 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Models/Answer.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 66.67% covered (warning) +
+
+
66.67%
2 / 3
CRAP
+
+ 94.44% covered (success) +
+
+
94.44%
17 / 18
Answer
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 66.67% covered (warning) +
+
+
66.67%
2 / 3
5.00
+
+ 94.44% covered (success) +
+
+
94.44%
17 / 18
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 setQuestionAnswers
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.00
+
+ 92.31% covered (success) +
+
+
92.31%
12 / 13
 jsonSerialize
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Models;
4
5use LDC\LDCCRM\Enums\ApiStatus;
6use LDC\LDCCRM\Utils;
7
8/**
9 * This class represents an answer
10 * It has a method to submit answers
11 *
12 * @property string $id The answer id
13 * @property string $question The actual question as a string
14 * @property \LDC\LDCCRM\Enums\QuestionType $type
15 * The question type to display,
16 * it also hints wether it can accept one or multiple responses,
17 * so only checkbox allows multiple responses
18 * @property \LDC\LDCCRM\Models\Answer[] $answers An array with answers
19 *
20 * @category Api Models
21 * @package LDCCrmClient
22 * @subpackage models
23 * @author Jérémy Munsch <jeremy.munsch@ldc.fr>
24 */
25class Answer extends CrmClientModel
26{
27    /**
28     * The question id
29     *
30     * Required
31     * Field Type bigInt
32     *
33     * @var integer
34     */
35    private $id;
36
37    /**
38     * The actual answer as a string
39     *
40     * Required
41     * Max length 255
42     * Field Type varchar
43     *
44     * @var string
45     */
46    private $answer = null;
47
48    /**
49     * The crm customer fields
50     *
51     * @var array
52     */
53    protected $modelFields = [
54        'id', 'answer'
55    ];
56
57    /**
58     * Validation rules
59     *
60     * @var array
61     */
62    protected static $validation = [
63        'id' => 'required|int',
64        'answer' => 'required|string|max:255'
65    ];
66
67    /**
68     * Answer
69     *
70     * @param array $properties
71     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
72     */
73    public function __construct(array $properties = [])
74    {
75        parent::__construct();
76        $this->fill($properties);
77        return $this;
78    }
79
80    // * PUBLIC METHODS
81
82    /**
83     * Set unanswered question for a specific customer
84     *
85     * @param string  $hashcode   The customer hashcode.
86     * @param integer $questionId The question id.
87     * @param array   $answersId  An array with answers id.
88     * @return boolean true if succeeded
89     *
90     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a hashcode is not a valid UUID.
91     * @throws LDC\LDCCRM\Exceptions\ValidationException If any of the provided ids are invalid.
92     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error \LDC\LDCCRM\Enums\ApiError occurs.
93     * Possibles are : - validationFailed, If given ids are wrong(CRM side).
94     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
95     */
96    public static function setQuestionAnswers(string $hashcode, int $questionId, array $answersId): bool
97    {
98        self::assertValidHascode($hashcode);
99        self::assertValidPrimaryKey($questionId);
100        if (!count($answersId)) {
101            return true;
102        }
103        foreach ($answersId as $answerId) {
104            self::assertValidPrimaryKey($answerId);
105        }
106        static::setUpApiInterface();
107        $request = Utils::getResquest(
108            'apiV2.customers.answers.store',
109            [
110                'serviceCustomer' => $hashcode
111            ],
112            [
113                'question' => $questionId,
114                'answers' => $answersId
115            ]
116        );
117
118        $response = static::$apiInterface->call($request);
119        return $response->status->equals(ApiStatus::OK());
120    }
121
122    /**
123     * Serialize this object to an array
124     *
125     * @return array
126     */
127    public function jsonSerialize()
128    {
129        return [
130            'id' => $this->id,
131            'answer' => $this->answer
132        ];
133    }
134}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Models/ArcadiaMailDefault.php.html b/tests/coverage/clients/V2/src/Models/ArcadiaMailDefault.php.html new file mode 100644 index 0000000..41280e7 --- /dev/null +++ b/tests/coverage/clients/V2/src/Models/ArcadiaMailDefault.php.html @@ -0,0 +1,689 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Models/ArcadiaMailDefault.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 71.43% covered (warning) +
+
+
71.43%
5 / 7
CRAP
+
+ 92.86% covered (success) +
+
+
92.86%
52 / 56
ArcadiaMailDefault
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 71.43% covered (warning) +
+
+
71.43%
5 / 7
21.16
+
+ 92.86% covered (success) +
+
+
92.86%
52 / 56
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 __get
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 __set
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
 fill
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6.40
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
 validateAllValues
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.10
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
 send
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
 jsonSerialize
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
15 / 15
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Models;
4
5use LDC\LDCCRM\Enums\ApiStatus;
6use LDC\LDCCRM\Enums\ArcadiaOrderType;
7use LDC\LDCCRM\Utils;
8use TypeError;
9
10/**
11 * This class represents the customer info with
12 * the account validation status
13 *
14 * @property string $email Must be a rfc valid email on a valid domain name
15 * @property string $firstname The account first name
16 * @property string $lastname The account last name
17 * @property string $phone The account phone
18 * @property string $address The account address (street number, name, build, flat number)
19 * @property string $address2 The account phone (address complement)
20 * @property string $city The account city name
21 * @property string $postcode The account city postcode
22 *
23 * @property int $operation The arcadia operation code
24 * @property int $support_code The arcadia support code
25 * @property string $label_type_support The arcadia type support label
26 * @property string $order_date The arcadia order date  'yyyy-mm-dd HH:ii:ss'
27 * @property int $order_num The arcadia order number
28 * @property \LDC\LDCCRM\Enums\ArcadiaOrderType $state_order The arcadia order date
29 * @property int $package_unit_weight The arcadia package unit weight
30 *
31 * @category Api Models
32 * @package LDCCrmClient
33 * @subpackage models
34 * @author Jérémy Munsch <jeremy.munsch@ldc.fr>
35 */
36class ArcadiaMailDefault extends CrmClientModel
37{
38    /**
39     * An RFC Valid Email
40     *
41     * Validate: rfc,dns,spoof,filter
42     *
43     * Required
44     * Min length N/A
45     * Max length 255
46     * Field Type varchar
47     *
48     * @var string
49     */
50    private $email = null;
51
52    /**
53     * First name
54     *
55     * Required
56     * Min length 2
57     * Max length 255
58     * Field Type varchar
59     *
60     * @var string
61     */
62    private $firstname = null;
63
64    /**
65     * Last name
66     *
67     * Required
68     * Min length 2
69     * Max length 255
70     * Field Type varchar
71     *
72     * @var string
73     */
74    private $lastname = null;
75
76    /**
77     * The user phone
78     *
79     * Validate: sometimes,string
80     *
81     * Required
82     * Min length NA
83     * Max length 255
84     * Field Type varchar
85     *
86     * @var string
87     */
88    private $phone = null;
89
90    /**
91     * The user address (street number, name, etc)
92     *
93     * Validate: sometimes,string
94     *
95     * Min length NA
96     * Max length 255
97     * Field Type varchar
98     *
99     * @var string
100     */
101    private $address = null;
102
103    /**
104     * The user address 2nd part (street number, name, more, etc)
105     *
106     * Validate: sometimes,string
107     *
108     * Min length NA
109     * Max length 255
110     * Field Type varchar
111     *
112     * @var string
113     */
114    private $address2 = null;
115
116    /**
117     * The user address city
118     *
119     * Validate: sometimes,string
120     *
121     * Min length NA
122     * Max length 255
123     * Field Type varchar
124     *
125     * @var string
126     */
127    private $city = null;
128
129    /**
130     * The user address postcode
131     *
132     * Validate: string
133     *
134     * Required
135     * Min length NA
136     * Max length 255
137     * Field Type varchar
138     *
139     * @var string
140     */
141    private $postcode = null;
142
143    /**
144     * The arcadia operation code
145     *
146     * Validate: int
147     *
148     * Required
149     *
150     * @var integer
151     */
152    private $operation = null;
153
154    /**
155     * The arcadia support code
156     *
157     * Validate: int
158     *
159     * Required
160     *
161     * @var integer
162     */
163    private $support_code = null;
164
165    /**
166     * The arcadia type support label
167     *
168     * Validate: string
169     *
170     * Required
171     *
172     * @var string
173     */
174    private $label_type_support = null;
175
176    /**
177     * The arcadia order date
178     *
179     * Validate: date
180     *
181     * Required
182     *
183     * @var string
184     */
185    private $order_date = null;
186
187    /**
188     * The arcadia order number
189     *
190     * Validate: numeric,digits:9
191     *
192     * Required
193     *
194     * @var integer
195     */
196    private $order_num = null;
197
198    /**
199     * The arcadia state order
200     *
201     * Validate: in ArcadiaOrderType::toValues()
202     *
203     * Required
204     *
205     * @var integer
206     */
207    private $state_order = null;
208
209    /**
210     * The arcadia package unit weight
211     *
212     * Validate: integer
213     *
214     * Required
215     *
216     * @var integer
217     */
218    private $package_unit_weight = null;
219
220    /**
221     * The crm customer fields
222     *
223     * @var array
224     */
225    protected $modelFields = [
226        'email', 'firstname',
227        'lastname', 'phone', 'address',
228        'address2', 'city', 'postcode',
229        'operation', 'support_code', 'label_type_support',
230        'order_date', 'order_num', 'state_order',
231        'package_unit_weight',
232    ];
233
234    /**
235     * Validation rules
236     *
237     * @var array
238     */
239    protected static $validation = [
240        'email' => 'required|email',
241        'firstname' => 'required|string|min:2|max:255',
242        'lastname' => 'required|string|min:2|max:255',
243        'phone' => 'sometimes|string|min:10|max:255',
244        'address' => 'sometimes|string|max:255',
245        'address2' => 'sometimes|string|max:255',
246        'city' => 'sometimes|string|max:255',
247        'postcode' => 'sometimes|string|max:255',
248        'operation' => 'required|int',
249        'support_code' => 'required|int',
250        'label_type_support' => 'required|string|min:2|max:255',
251        'order_date' => 'required|date_format:Y/m/d H:i:s',
252        'order_num' => 'required|int',
253        'state_order' => ['required'],
254        'package_unit_weight' => 'required|int'
255    ];
256
257    /**
258     * ArcadiaMailDefault
259     *
260     * @param array $properties
261     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
262     */
263    public function __construct(array $properties = [])
264    {
265        parent::__construct();
266        $this->fill($properties);
267        return $this;
268    }
269
270    /**
271     * Generic accessor
272     *
273     * @param string $field
274     * @return mixed
275     */
276    public function __get(string $field)
277    {
278        $props = \array_diff(
279            $this->modelFields,
280            $this->invisibleFields
281        );
282        if (in_array($field, $props)) {
283            if ($field === 'state_order' and !\is_object($this->{$field})) {
284                return ArcadiaOrderType::make($this->{$field});
285            }
286        }
287        return parent::__get($field);
288    }
289
290    /**
291     * Set prop
292     *
293     * @param string $field
294     * @param mixed  $value
295     * @return void
296     * @throws \TypeError If the field has an incorrect value.
297     * @throws \LDC\LDCCRM\Exceptions\AccessError If trying to set a value on a unknown property.
298     */
299    public function __set(string $field, $value): void
300    {
301        if ($field === 'state_order') {
302            $value = (\is_object($value) and
303                get_class($value) === ArcadiaOrderType::class) ? $value->value : $value;
304        }
305        parent::__set($field, $value);
306    }
307
308    /**
309     * Fill data into this model
310     * using validation rules
311     *
312     * @param array $properties
313     * @return void
314     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
315     */
316    public function fill(array $properties)
317    {
318        if (isset($properties['state_order'])) {
319            try {
320                $properties['state_order'] = (\is_object($properties['state_order']) and
321                    get_class($properties['state_order']) === ArcadiaOrderType::class) ?
322                    $properties['state_order']->value : (ctype_digit($properties['state_order']) ?
323                        (int)$properties['state_order'] : $properties['state_order']);
324            } catch (TypeError | \BadMethodCallException $e) {
325                // * Cast anyway
326                $properties['state_order'] = (int)$properties['state_order'];
327            }
328        }
329        parent::fill($properties);
330    }
331
332    /**
333     * Validate all model properties
334     *
335     * @param array $ignore
336     * @return array the validated values
337     * @throws ValidationException If a field could not be validated.
338     */
339    protected function validateAllValues(array $ignore = []): array
340    {
341        $out = [];
342        if (!\in_array('state_order', $ignore)) {
343            try {
344                $this->validateValue(
345                    'state_order',
346                    ArcadiaOrderType::make(Utils::getRawProperty($this, 'state_order'))
347                );
348            } catch (TypeError | \BadMethodCallException $e) {
349                // Use this to throw validation error.
350                $this->validateValue('state_order', Utils::getRawProperty($this, 'state_order'));
351            }
352            $out['state_order'] = Utils::getRawProperty($this, 'state_order');
353        }
354        return array_merge($out, parent::validateAllValues(array_merge(['state_order'], $ignore)));
355    }
356
357    // * PUBLIC METHODS
358
359    /**
360     * Send The actual mail
361     *
362     * @return boolean true if succeeded
363     * @throws ValidationException If a field could not be validated.
364     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
365     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
366     * Possibles are : - validationFailed, If provided data is wrong(CRM side).
367     */
368    public function send(): bool
369    {
370        $this->validateAllValues();
371        static::setUpApiInterface();
372        $request  = Utils::getResquest('apiV2.arcadia.outgoingMailDefault', [], $this->jsonSerialize());
373        $response = static::$apiInterface->call($request);
374        if ($status = $response->status->equals(ApiStatus::OK())) {
375            $this->updatedValues = [];
376        }
377        $this->isNew = !$status;
378        return $status;
379    }
380
381    /**
382     * Serialize this object to an array
383     *
384     * @return array
385     */
386    public function jsonSerialize()
387    {
388        return [
389            'deliver' => [
390                'email' => $this->email,
391                'firstname' => $this->firstname,
392                'lastname' => $this->lastname,
393                'phone' => $this->phone,
394                'address' => $this->address,
395                'address2' => $this->address2,
396                'city' => $this->city,
397                'postcode' => $this->postcode,
398            ],
399            'operation' => $this->operation,
400            'support_code' => $this->support_code,
401            'label_type_support' => $this->label_type_support,
402            'order_date' => $this->order_date,
403            'order_num' => $this->order_num,
404            'state_order' => $this->state_order,
405            'package_unit_weight' => $this->package_unit_weight
406        ];
407    }
408}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Models/ArcadiaMailGame.php.html b/tests/coverage/clients/V2/src/Models/ArcadiaMailGame.php.html new file mode 100644 index 0000000..f7d5432 --- /dev/null +++ b/tests/coverage/clients/V2/src/Models/ArcadiaMailGame.php.html @@ -0,0 +1,589 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Models/ArcadiaMailGame.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 60.00% covered (warning) +
+
+
60.00%
3 / 5
CRAP
+
+ 79.49% covered (warning) +
+
+
79.49%
31 / 39
ArcadiaMailGame
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 60.00% covered (warning) +
+
+
60.00%
3 / 5
15.69
+
+ 79.49% covered (warning) +
+
+
79.49%
31 / 39
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 __set
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
5.02
+
+ 60.00% covered (warning) +
+
+
60.00%
3 / 5
 fill
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
16.67
+
+ 33.33% covered (danger) +
+
+
33.33%
3 / 9
 send
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
 jsonSerialize
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Models;
4
5use LDC\LDCCRM\Enums\ApiStatus;
6use LDC\LDCCRM\Enums\ArcadiaOrderType;
7use LDC\LDCCRM\Utils;
8use TypeError;
9
10/**
11 * This class represents the customer info with
12 * the account validation status
13 *
14 * @property int $operation The arcadia operation code
15 *                          Validate: int
16 *                          Required
17 *
18 * @property string $email Must be a rfc valid email on a valid domain name
19 * @property string $firstname The account first name
20 * @property string $lastname The account last name
21 * @property string $phone The account phone
22 * @property string $address The account address (street number, name, build, flat number)
23 * @property string $address2 The account phone (address complement)
24 * @property string $city The account city name
25 * @property string $postcode The account city postcode
26 *
27 * @property int $support_code The arcadia support code
28 * @property string $label_type_support The arcadia type support label
29 * @property string $order_date The arcadia order date  'yyyy-mm-dd HH:ii:ss'
30 * @property string $code_unique The arcadia code unique
31 * @property string $code_on_pack The arcadia code on pack
32 *
33 * @category Api Models
34 * @package LDCCrmClient
35 * @subpackage models
36 * @author Jérémy Munsch <jeremy.munsch@ldc.fr>
37 */
38class ArcadiaMailGame extends CrmClientModel
39{
40    /**
41     * An RFC Valid Email
42     *
43     * Validate: rfc,dns,spoof,filter
44     *
45     * Required
46     * Min length N/A
47     * Max length 255
48     * Field Type varchar
49     *
50     * @var string
51     */
52    private $email = null;
53
54    /**
55     * First name
56     *
57     * Required
58     * Min length 2
59     * Max length 255
60     * Field Type varchar
61     *
62     * @var string
63     */
64    private $firstname = null;
65
66    /**
67     * Last name
68     *
69     * Required
70     * Min length 2
71     * Max length 255
72     * Field Type varchar
73     *
74     * @var string
75     */
76    private $lastname = null;
77
78    /**
79     * The user phone
80     *
81     * Validate: sometimes,string
82     *
83     * Required
84     * Min length NA
85     * Max length 255
86     * Field Type varchar
87     *
88     * @var string
89     */
90    private $phone = null;
91
92    /**
93     * The user address (street number, name, etc)
94     *
95     * Validate: sometimes,string
96     *
97     * Min length NA
98     * Max length 255
99     * Field Type varchar
100     *
101     * @var string
102     */
103    private $address = null;
104
105    /**
106     * The user address 2nd part (street number, name, more, etc)
107     *
108     * Validate: sometimes,string
109     *
110     * Min length NA
111     * Max length 255
112     * Field Type varchar
113     *
114     * @var string
115     */
116    private $address2 = null;
117
118    /**
119     * The user address city
120     *
121     * Validate: sometimes,string
122     *
123     * Min length NA
124     * Max length 255
125     * Field Type varchar
126     *
127     * @var string
128     */
129    private $city = null;
130
131    /**
132     * The user address postcode
133     *
134     * Validate: string
135     *
136     * Required
137     * Min length NA
138     * Max length 255
139     * Field Type varchar
140     *
141     * @var string
142     */
143    private $postcode = null;
144
145    /**
146     * The arcadia operation code
147     *
148     * Validate: int
149     *
150     * Required
151     *
152     * @var integer
153     */
154    private $operation = null;
155
156    /**
157     * The arcadia support code
158     *
159     * Validate: int
160     *
161     * Required
162     *
163     * @var integer
164     */
165    private $support_code = null;
166
167    /**
168     * The arcadia type support label
169     *
170     * Validate: string
171     *
172     * Required
173     *
174     * @var string
175     */
176    private $label_type_support = null;
177
178    /**
179     * The arcadia order date
180     *
181     * Validate: date
182     *
183     * Required
184     *
185     * @var string
186     */
187    private $order_date = null;
188
189    /**
190     * The arcadia code unique
191     *
192     * Validate: string
193     *
194     * Required
195     *
196     * @var string
197     */
198    private $code_unique = null;
199
200    /**
201     * The arcadia code on pack
202     *
203     * Validate: sometimes
204     *
205     * Required
206     *
207     * @var string
208     */
209    private $code_on_pack = null;
210
211    /**
212     * The crm customer fields
213     *
214     * @var array
215     */
216    protected $modelFields = [
217        'email', 'firstname',
218        'lastname', 'phone', 'address',
219        'address2', 'city', 'postcode',
220        'operation', 'support_code', 'label_type_support',
221        'order_date', 'code_unique', 'code_on_pack'
222    ];
223
224    /**
225     * Validation rules
226     *
227     * @var array
228     */
229    protected static $validation = [
230        'email' => 'required|email',
231        'firstname' => 'required|string|min:2|max:255',
232        'lastname' => 'required|string|min:2|max:255',
233        'phone' => 'sometimes|string|min:10|max:255',
234        'address' => 'sometimes|string|max:255',
235        'address2' => 'sometimes|string|max:255',
236        'city' => 'sometimes|string|max:255',
237        'postcode' => 'sometimes|string|max:255',
238        'operation' => 'required|int',
239        'support_code' => 'required|int',
240        'label_type_support' => 'required|string|min:2|max:255',
241        'order_date' => 'required|date_format:Y/m/d H:i:s',
242        'code_unique' => 'required|string',
243        'code_on_pack' => 'sometimes|string'
244    ];
245
246    /**
247     * ArcadiaMailGame
248     *
249     * @param array $properties
250     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
251     */
252    public function __construct(array $properties = [])
253    {
254        parent::__construct();
255        $this->fill($properties);
256        return $this;
257    }
258
259    /**
260     * Set prop
261     *
262     * @param string $field
263     * @param mixed  $value
264     * @return void
265     * @throws \TypeError If the field has an incorrect value.
266     * @throws \LDC\LDCCRM\Exceptions\AccessError If trying to set a value on a unknown property.
267     */
268    public function __set(string $field, $value): void
269    {
270        if ($field === 'state_order') {
271            $value = (\is_object($value) and
272                get_class($value) === ArcadiaOrderType::class) ? $value->value : $value;
273        }
274        parent::__set($field, $value);
275    }
276
277    /**
278     * Fill data into this model
279     * using validation rules
280     *
281     * @param array $properties
282     * @return void
283     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
284     */
285    public function fill(array $properties)
286    {
287        if (isset($properties['state_order'])) {
288            try {
289                $properties['state_order'] = (\is_object($properties['state_order']) and
290                    get_class($properties['state_order']) === ArcadiaOrderType::class) ?
291                    $properties['state_order']->value : (ctype_digit($properties['state_order']) ?
292                    (int)$properties['state_order'] : $properties['state_order']);
293            } catch (TypeError | \BadMethodCallException $e) {
294                // * Cast anyway
295                $properties['state_order'] = (int)$properties['state_order'];
296            }
297        }
298        parent::fill($properties);
299    }
300
301    // * PUBLIC METHODS
302
303    /**
304     * Send The actual mail
305     *
306     * @return boolean true if succeeded
307     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
308     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
309     * Possibles are : - validationFailed, If provided data is wrong(CRM side).
310     */
311    public function send(): bool
312    {
313        $this->validateAllValues();
314        static::setUpApiInterface();
315        $request  = Utils::getResquest('apiV2.arcadia.outgoingMailGame', [], $this->jsonSerialize());
316        $response = static::$apiInterface->call($request);
317        if ($status = $response->status->equals(ApiStatus::OK())) {
318            $this->updatedValues = [];
319        }
320        $this->isNew = !$status;
321        return $status;
322    }
323
324    /**
325     * Serialize this object to an array
326     *
327     * @return array
328     */
329    public function jsonSerialize()
330    {
331        return [
332            'deliver' => [
333                'email' => $this->email,
334                'firstname' => $this->firstname,
335                'lastname' => $this->lastname,
336                'phone' => $this->phone,
337                'address' => $this->address,
338                'address2' => $this->address2,
339                'city' => $this->city,
340                'postcode' => $this->postcode,
341            ],
342            'operation' => $this->operation,
343            'support_code' => $this->support_code,
344            'label_type_support' => $this->label_type_support,
345            'order_date' => $this->order_date,
346            'code_unique' => $this->code_unique,
347            'code_on_pack' => $this->code_on_pack
348        ];
349    }
350}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Models/CrmClientModel.php.html b/tests/coverage/clients/V2/src/Models/CrmClientModel.php.html new file mode 100644 index 0000000..da19a27 --- /dev/null +++ b/tests/coverage/clients/V2/src/Models/CrmClientModel.php.html @@ -0,0 +1,736 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Models/CrmClientModel.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 41.67% covered (danger) +
+
+
41.67%
5 / 12
CRAP
+
+ 78.79% covered (warning) +
+
+
78.79%
78 / 99
CrmClientModel
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 41.67% covered (danger) +
+
+
41.67%
5 / 12
73.86
+
+ 78.79% covered (warning) +
+
+
78.79%
78 / 99
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 __get
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
 __set
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
 setUpApiInterface
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 fill
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
20.42
+
+ 52.94% covered (warning) +
+
+
52.94%
9 / 17
 fillRaw
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
7.46
+
+ 40.00% covered (danger) +
+
+
40.00%
4 / 10
 validateAllValues
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 validateValue
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.01
+
+ 85.71% covered (warning) +
+
+
85.71%
6 / 7
 checkFieldType
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12.23
+
+ 88.24% covered (warning) +
+
+
88.24%
15 / 17
 getPropType
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
4.18
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
 assertValidPrimaryKey
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.02
+
+ 83.33% covered (warning) +
+
+
83.33%
5 / 6
 assertValidHascode
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.02
+
+ 83.33% covered (warning) +
+
+
83.33%
5 / 6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Models;
4
5use JsonSerializable;
6use LDC\LDCCRM\Enums\ArcadiaOrderType;
7use LDC\LDCCRM\Enums\Country;
8use LDC\LDCCRM\Enums\LogLevel;
9use LDC\LDCCRM\Enums\QuestionType;
10use LDC\LDCCRM\Exceptions\AccessError;
11use LDC\LDCCRM\Exceptions\ValidationException;
12use LDC\LDCCRM\Transport\ApiInterface;
13use LDC\LDCCRM\Utils;
14
15/**
16 * @ignore
17 * Base Class for crm client models
18 *
19 * @category Api Models
20 * @package LDCCrmClient
21 * @subpackage models
22 * @author Jérémy Munsch <jeremy.munsch@ldc.fr>
23 */
24abstract class CrmClientModel implements JsonSerializable
25{
26    /**
27     * Is true if this model was not filled from crm
28     *
29     * @var boolean
30     */
31    protected $isNew = true;
32
33    /**
34     * The Api object that will handle requests and responses
35     *
36     * @var \LDC\LDCCRM\Transport\ApiInterface
37     */
38    protected static $apiInterface;
39
40    /**
41     * Validation rules
42     *
43     * @var array
44     */
45    protected static $validation = [];
46
47    /**
48     * Model fields
49     *
50     * @var array
51     */
52    protected $modelFields = [];
53
54    /**
55     * The crm fields
56     * that should not be visible
57     *
58     * @var array
59     */
60    protected $invisibleFields = [];
61
62    /**
63     * Model updated values
64     *
65     * @var array
66     */
67    protected $updatedValues = [];
68
69    /**
70     * CrmClientModel
71     */
72    public function __construct()
73    {
74        self::setUpApiInterface();
75        return $this;
76    }
77
78    /**
79     * Generic accessor
80     *
81     * @param string $field
82     * @return mixed|null Returns null if the property was not found any way.
83     */
84    public function __get(string $field)
85    {
86        $props = \array_diff(
87            $this->modelFields,
88            $this->invisibleFields
89        );
90        if (in_array($field, $props)) {
91            return Utils::getRawProperty($this, $field);
92        }
93        return null;
94    }
95
96    /**
97     * Set prop on the model.
98     *
99     * @param string $field
100     * @param mixed  $value
101     * @return void
102     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
103     * @throws \LDC\LDCCRM\Exceptions\AccessError If trying to set a value on a unknown property.
104     */
105    public function __set(string $field, $value): void
106    {
107        try {
108            if (in_array($field, $this->modelFields)) {
109                $type = $this->getPropType($field);
110                if (!is_null($value) and !$this->checkFieldType($type, $value)) {
111                    throw new ValidationException("Wrong type for $field");
112                }
113                $this->validateValue($field, $value);
114                $this->updatedValues[$field] = $value;
115                Utils::setRawProperty($this, $field, $value);
116                return;
117            }
118            // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
119        } catch (\ReflectionException $e) {
120            // Do nothing.
121        }
122        throw new AccessError(\get_class($this), $field);
123    }
124
125    /**
126     * Create the Api interface
127     *
128     * @return void
129     */
130    protected static function setUpApiInterface()
131    {
132        self::$apiInterface = self::$apiInterface ?: new ApiInterface();
133    }
134
135    /**
136     * Fill values into model using validation rules.
137     *
138     * @param array $properties
139     * @return void
140     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
141     */
142    public function fill(array $properties)
143    {
144        if (count($properties)) {
145            foreach ($properties as $field => $value) {
146                try {
147                    $type = $this->getPropType($field);
148                    // * Force boolean
149                    if ($type === 'bool') {
150                        $value = (\is_int($value) and ($value === 1 or $value === 0)) ? (bool)$value : $value;
151                    }
152                    if (!is_null($value) and !$this->checkFieldType($type, $value)) {
153                        throw new ValidationException("Wrong type for $field");
154                    }
155                    $this->validateValue($field, $value);
156                    $this->updatedValues[$field] = $value;
157                    Utils::setRawProperty($this, $field, $value);
158                } catch (AccessError | \ReflectionException $e) {
159                    Utils::log(\sprintf(
160                        '%s::fill : unknown property access for %s, it does not exist on object.',
161                        \get_class($this),
162                        $field
163                    ), LogLevel::DEBUG());
164                    continue;
165                }
166            } //end foreach
167        }//end if
168    }
169
170    /**
171     * Set all properties to without any validation
172     *
173     * @param array $properties
174     * @return void
175     */
176    protected function fillRaw(array $properties)
177    {
178        if (count($properties)) {
179            foreach ($properties as $field => $value) {
180                try {
181                    Utils::setRawProperty($this, $field, $value);
182                } catch (AccessError $e) {
183                    Utils::log(\sprintf(
184                        '%s::fillRaw : unknown property access for %s, it does not exist on object.',
185                        \get_class($this),
186                        $field
187                    ), LogLevel::DEBUG());
188                    continue;
189                }
190            }
191        }
192    }
193
194    /**
195     * Validate all model properties
196     *
197     * * All properties are nullable
198     *
199     * @param array $ignore
200     * @return array the validated values
201     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
202     */
203    protected function validateAllValues(array $ignore = []): array
204    {
205        $out = [];
206        foreach (static::$validation as $field => $validationRules) {
207            if (\in_array($field, $ignore) or \is_null(Utils::getRawProperty($this, $field))) {
208                continue;
209            }
210            $this->validateValue($field, Utils::getRawProperty($this, $field));
211            $out[$field] = Utils::getRawProperty($this, $field);
212        }
213        return $out;
214    }
215
216    /**
217     * Validate a value against its rules
218     *
219     * @param string $field
220     * @param mixed  $value
221     * @return boolean
222     * @throws \ErrorException If a field has no validation rule.
223     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
224     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
225     */
226    protected static function validateValue(string $field, $value): bool
227    {
228        if (!isset(static::$validation[$field])) {
229            throw new \ErrorException("$field has no validation rule");
230        }
231        $validator = Utils::makeValidator(
232            [$field => $value],
233            [$field => static::$validation[$field]]
234        );
235        /** @throws \LDC\LDCCRM\Exceptions\ValidationException */
236        $validator();
237        return true;
238    }
239
240    /**
241     * Check a value against its field type
242     *
243     * @param string $type
244     * @param mixed  $value
245     * @return boolean Return true if $type matches $value type.
246     * @throws \ErrorException If type is not handled,
247     * handled types are boolean, string, integer, \LDC\LDCCRM\Enums\Country,  \LDC\LDCCRM\Enums\ArcadiaOrderType .
248     * @phpcs:ignore Generic.Files.LineLength.TooLong
249     */
250    protected function checkFieldType(string $type, $value): bool
251    {
252        /**
253         * Checks if the value is valid for a specific enum class
254         * @param string $className
255         * @param mixed  $value
256         * @return boolean
257         */
258        $checkValueIsEnum = function (string $className, $value) {
259            try {
260                $value = \is_int($value) ? $className::make($value) : $value;
261            } finally {
262                return \is_object($value) and (get_class($value) === $className or
263                    \in_array($className, \class_parents($value)));
264            }
265        };
266
267        switch ($type) {
268            case '\\' . Country::class:
269            case '\\' . QuestionType::class:
270            case '\\' . ArcadiaOrderType::class:
271                return $checkValueIsEnum(\ltrim($type, '\\'), $value);
272            case 'boolean':
273                return \is_bool(\is_int($value) ? (bool)$value : $value);
274            case 'array':
275                return \is_array($value);
276            case 'string':
277                return \is_string($value);
278            case 'integer':
279                return \is_int($value);
280        }
281        throw new \ErrorException(\sprintf("Unhandled $type for %s", \json_encode($value)));
282    }
283
284    /**
285     * Get the type of any of this class property
286     * from doc comment
287     *
288     * @param string $property
289     * @return string|null the type or null if not defined
290     * @throws \ErrorException If the property has no comment to read.
291     * @throws \ErrorException If a type for a property could ne be recognized.
292     * @throws \ReflectionException If the class or property does not exist.
293     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
294     */
295    protected function getPropType(string $property)
296    {
297        $matches        = [];
298        $propReflection = new \ReflectionProperty(static::class, $property);
299        $docComment     = $propReflection->getDocComment();
300        if (!$docComment) {
301            throw new \ErrorException(\sprintf("%s $property has not comment to parse.", \get_class($this)));
302        }
303        preg_match_all('/@var ([\w_\x5c]+)/', $docComment, $matches);
304        if (
305            isset($matches[1][0]) and \in_array($matches[1][0], [
306                'string', 'integer', 'boolean', 'array', '\\' . Country::class, '\\' . ArcadiaOrderType::class,
307            '\\' . QuestionType::class
308            ], true)
309        ) {
310            return $matches[1][0];
311        }
312        throw new \ErrorException("Unknown type for property $property");
313    }
314
315    /**
316     * Assert primaryKey is valid.
317     *
318     * @param mixed $primaryKey
319     * @return void
320     * @throws LDC\LDCCRM\Exceptions\ValidationException If a primary key is not a valid integer.
321     */
322    protected static function assertValidPrimaryKey($primaryKey): void
323    {
324        $validator = Utils::makeValidator(
325            ['id' => $primaryKey],
326            ['id' => 'required|int']
327        );
328        if ($validator()) {
329            throw new ValidationException(implode(' ', $validator->errors()->get('id')));
330        }
331    }
332
333    /**
334     * Assert hashcode is valid.
335     *
336     * @param mixed $hashcode
337     * @return void
338     * @throws LDC\LDCCRM\Exceptions\ValidationException If a hashcode is not a valid UUID.
339     */
340    protected static function assertValidHascode($hashcode): void
341    {
342        $validator = Utils::makeValidator(
343            ['hashcode' => $hashcode],
344            ['hashcode' => 'required|uuid']
345        );
346        if ($validator()) {
347            throw new ValidationException(implode(' ', $validator->errors()->get('hashcode')));
348        }
349    }
350}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Models/Customer.php.html b/tests/coverage/clients/V2/src/Models/Customer.php.html new file mode 100644 index 0000000..4268f2e --- /dev/null +++ b/tests/coverage/clients/V2/src/Models/Customer.php.html @@ -0,0 +1,1069 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Models/Customer.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 73.33% covered (warning) +
+
+
73.33%
11 / 15
CRAP
+
+ 94.59% covered (success) +
+
+
94.59%
105 / 111
Customer
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 73.33% covered (warning) +
+
+
73.33%
11 / 15
35.19
+
+ 94.59% covered (success) +
+
+
94.59%
105 / 111
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 __get
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 __set
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
 fill
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6.40
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
 validateAllValues
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.21
+
+ 71.43% covered (warning) +
+
+
71.43%
5 / 7
 find
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.01
+
+ 91.67% covered (success) +
+
+
91.67%
11 / 12
 findWithEmail
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 save
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
 store
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
 update
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
 delete
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
 activate
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 toggleOptin
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 login
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 jsonSerialize
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Models;
4
5use LDC\LDCCRM\Enums\ApiError;
6use LDC\LDCCRM\Enums\ApiStatus;
7use LDC\LDCCRM\Enums\Country;
8use LDC\LDCCRM\Exceptions\ApiErrorException;
9use LDC\LDCCRM\Utils;
10use TypeError;
11
12/**
13 * This class represents the customer info with
14 * the account validation status. You can use it as
15 * a standalone class but beware of catching exceptions
16 *
17 * Be warned that on server side data is normalized
18 * - firstname and lastname are 'capitalized'
19 * - fields are trimmed
20 * - non numeric chars are removed from postcode
21 *
22 * @property string $hashcode The crm hashcode this is a readonly field
23 * @property string $email Must be a rfc valid email on a valid domain name
24 * @property string $password Required only on create, must have upper and lowers chars,
25 * at least 1 number, at leat 1 upper char, at least 1 lower char and
26 * at leat 1 special char
27 * @property string $firstname The account first name
28 * @property string $lastname The account last name
29 * @property string $birth_date The account birth date 'yyyy-mm-dd'
30 * @property string $phone The account phone
31 * @property string $address The account address (street number, name, build, flat number)
32 * @property string $address2 The account phone (address complement)
33 * @property string $city The account city name
34 * @property string $postcode The account city postcode
35 * @property \LDC\LDCCRM\Enums\Country $country The account country residence
36 * @property bool $opt_in true if the account is opt in
37 * @property bool $status true if the account is activated
38 * @property string $last_connection the last time user was connected 'yyyy-mm-dd HH:ii:ss'
39 *
40 * @example examples/CustomerExample.php
41 *
42 * @category Api Models
43 * @package LDCCrmClient
44 * @subpackage models
45 * @author Jérémy Munsch <jeremy.munsch@ldc.fr>
46 */
47class Customer extends CrmClientModel
48{
49    /**
50     * The user hashcode, this is link with the customer and the brand it is related to
51     * It is used to identify the customer itself.
52     *
53     * Validate: UUID, in customer_infos
54     *
55     * Required
56     * Min length 36
57     * Max length 36
58     * Field Type varchar
59     *
60     * @var string
61     */
62    private $hashcode;
63
64    /**
65     * An RFC Valid Email
66     *
67     * Validate: rfc,dns,spoof,filter
68     *
69     * Required
70     * Min length N/A
71     * Max length 255
72     * Field Type varchar
73     *
74     * @var string
75     */
76    private $email = null;
77
78    /**
79     * The user password
80     * This is set only when creating a user
81     * Otherwise it has to be set to null
82     *
83     * Validate: case_diff,numbers,letters,symbols,min:8
84     *
85     * Min length 8
86     * Max length 255
87     * Field Type varchar
88     *
89     * @var string
90     */
91    private $password = null;
92
93    /**
94     * First name
95     *
96     * Required
97     * Min length 2
98     * Max length 255
99     * Field Type varchar
100     *
101     * @var string
102     */
103    private $firstname = null;
104
105    /**
106     * Last name
107     *
108     * Required
109     * Min length 2
110     * Max length 255
111     * Field Type varchar
112     *
113     * @var string
114     */
115    private $lastname = null;
116
117    /**
118     * Birth date
119     *
120     * Validate: date_format:Y-m-d
121     *
122     * Required
123     * Field Type Date
124     * @var string
125     */
126    private $birth_date = null;
127
128    /**
129     * The user phone
130     *
131     * Validate: sometimes,string
132     *
133     * Required
134     * Min length NA
135     * Max length 255
136     * Field Type varchar
137     *
138     * @var string
139     */
140    private $phone = null;
141
142    /**
143     * The user address (street number, name, etc)
144     *
145     * Validate: sometimes,string
146     *
147     * Min length NA
148     * Max length 255
149     * Field Type varchar
150     *
151     * @var string
152     */
153    private $address = null;
154
155    /**
156     * The user address 2nd part (street number, name, more, etc)
157     *
158     * Validate: sometimes,string
159     *
160     * Min length NA
161     * Max length 255
162     * Field Type varchar
163     *
164     * @var string
165     */
166    private $address2 = null;
167
168    /**
169     * The user address city
170     *
171     * Validate: sometimes,string
172     *
173     * Min length NA
174     * Max length 255
175     * Field Type varchar
176     *
177     * @var string
178     */
179    private $city = null;
180
181    /**
182     * The user address postcode
183     *
184     * Validate: string
185     *
186     * Required
187     * Min length NA
188     * Max length 255
189     * Field Type varchar
190     *
191     * @var string
192     */
193    private $postcode = null;
194
195    /**
196     * The user address postcode
197     *
198     * Validate: sometimes, in Country::toValues()
199     *
200     * Required
201     * Field Type tinyInt
202     *
203     * @var \LDC\LDCCRM\Enums\Country
204     */
205    private $country = null;
206
207    /**
208     * The user optin choice
209     *
210     * Validate: boolean
211     *
212     * Required
213     * Field Type TinyInt
214     *
215     * @var boolean
216     */
217    private $opt_in = false;
218
219    /**
220     * Is the account activated ?
221     *
222     * This field is read only
223     *
224     * @var boolean
225     */
226    private $status = false;
227
228    /**
229     * The last time the account was connected
230     *
231     * This field is read only
232     *
233     * @var string
234     */
235    private $last_connection = null;
236
237    /**
238     * The crm customer fields
239     *
240     * @var array
241     */
242    protected $modelFields = [
243        'hashcode', 'email', 'firstname', 'password',
244        'lastname', 'birth_date', 'phone', 'address',
245        'address2', 'city', 'postcode', 'country',
246        'opt_in', 'status', 'last_connection'
247    ];
248
249    /**
250     * The crm customer fields
251     * the should not be visible
252     *
253     * @var array
254     */
255    protected $invisibleFields = [
256        'password'
257    ];
258
259    /**
260     * Validation rules
261     *
262     * @var array
263     */
264    protected static $validation = [
265        'hashcode' => 'required|uuid',
266        'email' => 'required|email',
267        'firstname' => 'required|string|min:1|max:255',
268        'lastname' => 'required|string|min:1|max:255',
269        'password' => 'required|min:8|max:255|mixedCase|containDigit|containSymbol',
270        'birth_date' => 'required|date_format:Y-m-d',
271        'phone' => 'sometimes|nullable|string|max:255',
272        'address' => 'sometimes|string|max:255',
273        'address2' => 'sometimes|string|max:255',
274        'city' => 'sometimes|string|min:2|max:255',
275        'postcode' => 'sometimes|string|max:255',
276        'country' => ['sometimes'],
277        'opt_in' => 'required|bool',
278        'status' => 'required|bool',
279        'last_connection' => 'sometimes|date'
280    ];
281
282    /**
283     * Customer
284     *
285     * @param array $properties
286     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
287     */
288    public function __construct(array $properties = [])
289    {
290        parent::__construct();
291        $this->fill($properties);
292        return $this;
293    }
294
295    /**
296     * Generic accessor
297     *
298     * @param string $field
299     * @return mixed|null Returns null if the property was not found any way.
300     */
301    public function __get(string $field)
302    {
303        $props = \array_diff(
304            $this->modelFields,
305            $this->invisibleFields
306        );
307        if (in_array($field, $props)) {
308            if ($field === 'country' and !\is_object($this->{$field})) {
309                return Country::make($this->{$field});
310            }
311        }
312        return parent::__get($field);
313    }
314
315    /**
316     * Set prop on the model.
317     *
318     * @param string $field
319     * @param mixed  $value
320     * @return void
321     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
322     * @throws \LDC\LDCCRM\Exceptions\AccessError If trying to set a value on a unknown property.
323     */
324    public function __set(string $field, $value): void
325    {
326        if ($field === 'country') {
327            $value = (\is_object($value) and
328                get_class($value) === Country::class) ? $value->value : $value;
329        }
330        parent::__set($field, $value);
331    }
332
333    /**
334     * Fill values into model using validation rules.
335     *
336     * @param array $properties
337     * @return void
338     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
339     */
340    public function fill(array $properties)
341    {
342        if (isset($properties['country'])) {
343            try {
344                $properties['country'] = (\is_object($properties['country']) and
345                    get_class($properties['country']) === Country::class) ?
346                    $properties['country']->value : (ctype_digit($properties['country']) ?
347                        (int)$properties['country'] : $properties['country']);
348            } catch (TypeError | \BadMethodCallException $e) {
349                // * Cast anyway
350                $properties['country'] = (int)$properties['country'];
351            }
352        }
353        parent::fill($properties);
354    }
355
356    /**
357     * Validate all model properties
358     *
359     * @param array $ignore
360     * @return array the validated values
361     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
362     */
363    protected function validateAllValues(array $ignore = []): array
364    {
365        $out = [];
366        if (!\in_array('country', $ignore)) {
367            try {
368                $this->validateValue('country', Country::make(Utils::getRawProperty($this, 'country')));
369            } catch (\TypeError | \BadMethodCallException $e) {
370                // Use this to throw validation error.
371                $this->validateValue('country', Utils::getRawProperty($this, 'country'));
372            }
373            $out['country'] = Utils::getRawProperty($this, 'country');
374        }
375        return array_merge($out, parent::validateAllValues(array_merge(['country'], $ignore)));
376    }
377
378    // * PUBLIC METHODS
379
380    /**
381     * Find a customer using its hashcode in the crm
382     *
383     * @param string $hashcode
384     * @return self|null Return null if the customer was not found
385     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
386     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
387     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
388     */
389    public static function find(string $hashcode): ?self
390    {
391        static::setUpApiInterface();
392        $request  = Utils::getResquest('apiV2.customers.show', [
393            'serviceCustomer' => $hashcode
394        ]);
395        $customer = null;
396        try {
397            $response = static::$apiInterface->call($request);
398            $customer = new self();
399            // ! Use fillRaw here because we cant validate comming
400            // ! from CRM since it has crappy data comming from the old crm (jellix).
401            $customer->fillRaw($response->data);
402            Utils::setRawProperty($customer, 'isNew', false);
403        } catch (ApiErrorException $e) {
404            if (!$e->getApiError()->equals(ApiError::modelNotFound())) {
405                throw $e;
406            }
407        }
408        return $customer;
409    }
410
411    /**
412     * Find a customer using its email in the crm
413     * This is an alias of Customer::find()
414     *
415     * @param string $email
416     * @return self|null
417     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
418     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
419     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
420     */
421    public static function findWithEmail(string $email): ?self
422    {
423        return static::find($email);
424    }
425
426    /**
427     * Save the customer on CRM.
428     *
429     * @return boolean True if save was successfull.
430     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
431     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
432     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
433     * Possibles are : - validationFailed, If provided data is wrong(CRM side).
434     *                 - modelNotFound, If this customer hashcode could not be found (new customers only).
435     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
436     */
437    public function save(): bool
438    {
439        $saved = false;
440        if ($this->isNew) {
441            $saved               = $this->store();
442            $this->isNew         = !$saved;
443            $this->updatedValues = [];
444            if ($saved) {
445                Utils::setRawProperty($this, 'password', null);
446            }
447            return $saved;
448        }
449        if ($saved = $this->update()) {
450            $this->updatedValues = [];
451            Utils::setRawProperty($this, 'password', null);
452        }
453        return $saved;
454    }
455
456    /**
457     * Calls api store customer method
458     *
459     * @return boolean True if save was successfull.
460     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
461     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
462     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
463     * Possibles are : - validationFailed, If provided data is wrong(CRM side).
464     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
465     */
466    private function store(): bool
467    {
468        $data             = $this->validateAllValues(['hashcode', 'last_connection', 'status']);
469        $data['password'] = $this->password;
470
471        static::setUpApiInterface();
472        $request = Utils::getResquest('apiV2.customers.store', [], $data);
473
474        $response = static::$apiInterface->call($request);
475        if ($status = $response->status->equals(ApiStatus::OK())) {
476            $this->updatedValues = [];
477            $this->fill($response->data);
478        }
479        return $status;
480    }
481
482    /**
483     * Calls api update customer method
484     *
485     * @return boolean True if save was successfull.
486     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
487     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
488     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
489     * Possibles are : - validationFailed, If provided data is wrong(CRM side).
490     *                 - modelNotFound, If this customer hashcode could not be found.
491     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
492     */
493    private function update(): bool
494    {
495        $this->validateValue('hashcode', $this->hashcode);
496        static::setUpApiInterface();
497        $request = Utils::getResquest('apiV2.customers.update', [
498            'serviceCustomer' => $this->hashcode
499        ], $this->updatedValues);
500
501        $response = static::$apiInterface->call($request);
502        if ($status = $response->status->equals(ApiStatus::OK())) {
503            $this->updatedValues = [];
504            $this->fill($response->data);
505        }
506        return $status;
507    }
508
509    /**
510     * Removes the customer from CRM.
511     *
512     * @return boolean true if succeeded
513     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
514     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
515     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
516     * Possibles are : - modelNotFound, If this customer hashcode could not be found.
517     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
518     */
519    public function delete(): bool
520    {
521        $this->validateValue('hashcode', $this->hashcode);
522        static::setUpApiInterface();
523        $request = Utils::getResquest('apiV2.customers.destroy', [
524            'serviceCustomer' => $this->hashcode
525        ]);
526
527        $response = static::$apiInterface->call($request);
528        return $response->status->equals(ApiStatus::OK());
529    }
530
531    /**
532     * Active the customer on CRM.
533     *
534     * @return boolean true if succeeded
535     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a customer's hashcode could not be validated.
536     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
537     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
538     * Possibles are : - modelNotFound, If this customer hashcode could not be found.
539     *                 - customerAlreadyActive, It the customer is already activated.
540     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
541     */
542    public function activate(): bool
543    {
544        $this->validateValue('hashcode', $this->hashcode);
545        static::setUpApiInterface();
546        $request = Utils::getResquest('apiV2.customers.activate', [
547            'serviceCustomer' => $this->hashcode
548        ]);
549
550        $response = static::$apiInterface->call($request);
551        $this->__set('status', true);
552        return $response->status->equals(ApiStatus::OK());
553    }
554
555    /**
556     * Toggle the account optin value in the crm
557     *
558     * @return boolean true if succeeded
559     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
560     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
561     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
562     * Possibles are : - modelNotFound, If this customer hashcode could not be found.
563     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
564     */
565    public function toggleOptin(): bool
566    {
567        $this->__set('opt_in', !$this->opt_in);
568        return $this->update();
569    }
570
571    /**
572     * Test customer credentials
573     *
574     * @param string $email
575     * @param string $password
576     * @return boolean
577     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a customer's email could not be validated.
578     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
579     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
580     * Possibles are : - authFailed, If credentials are not valid.
581     *                 - customerNotActivated, If the account is not activated.
582     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
583     */
584    public static function login(string $email, string $password): bool
585    {
586        self::validateValue('email', $email);
587        static::setUpApiInterface();
588        $request  = Utils::getResquest('apiV2.customers.authenticate', [], [
589            'email' => $email,
590            'password' => $password
591        ]);
592        $response = static::$apiInterface->call($request);
593        return $response->status->equals(ApiStatus::OK());
594    }
595
596    /**
597     * Serialize this object to an array
598     *
599     * @return array
600     */
601    public function jsonSerialize()
602    {
603        return [
604            'hashcode' => $this->hashcode,
605            'email' => $this->email,
606            'firstname' => $this->firstname,
607            'lastname' => $this->lastname,
608            'birth_date' => $this->birth_date,
609            'phone' => $this->phone,
610            'address' => $this->address,
611            'address2' => $this->address2,
612            'city' => $this->city,
613            'postcode' => $this->postcode,
614            'country' => $this->country,
615            'opt_in' => $this->opt_in,
616            'status' => $this->status,
617            'last_connection' => $this->last_connection
618        ];
619    }
620}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Models/Question.php.html b/tests/coverage/clients/V2/src/Models/Question.php.html new file mode 100644 index 0000000..8a2de4e --- /dev/null +++ b/tests/coverage/clients/V2/src/Models/Question.php.html @@ -0,0 +1,631 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Models/Question.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 66.67% covered (warning) +
+
+
66.67%
6 / 9
CRAP
+
+ 82.05% covered (warning) +
+
+
82.05%
64 / 78
Question
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 66.67% covered (warning) +
+
+
66.67%
6 / 9
28.61
+
+ 82.05% covered (warning) +
+
+
82.05%
64 / 78
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 __get
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 __set
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
20
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
 fill
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6.40
+
+ 77.78% covered (warning) +
+
+
77.78%
7 / 9
 validateAllValues
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 7
 all
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
 allWithCustomerAnswers
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
19 / 19
 unansweredBy
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
 jsonSerialize
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Models;
4
5use LDC\LDCCRM\Enums\QuestionType;
6use LDC\LDCCRM\Utils;
7use TypeError;
8
9/**
10 * This class represents a question
11 * It has some static methods to retrieve questions and answers
12 *
13 * @property string $id The question id
14 * @property string $question The actual question as a string
15 * @property \LDC\LDCCRM\Enums\QuestionType $type
16 * The question type to display,
17 * it also hints wether it can accept one or multiple responses,
18 * so only checkbox allows multiple responses
19 * @property \LDC\LDCCRM\Models\Answer[] $answers An array with answers
20 *
21 * @category Api Models
22 * @package LDCCrmClient
23 * @subpackage models
24 * @author Jérémy Munsch <jeremy.munsch@ldc.fr>
25 */
26class Question extends CrmClientModel
27{
28    /**
29     * The question id
30     *
31     * Required
32     * Field Type bigInt
33     *
34     * @var integer
35     */
36    private $id;
37
38    /**
39     * The actual question as a string
40     *
41     * Required
42     * Max length 255
43     * Field Type varchar
44     *
45     * @var string
46     */
47    private $question = null;
48
49    /**
50     * The question type
51     *
52     * Validate: in QuestionType::toValues()
53     *
54     * Required
55     * Field Type tinyInteger
56     *
57     * @var \LDC\LDCCRM\Enums\QuestionType
58     */
59    private $type = null;
60
61    /**
62     * The question answers
63     *
64     * \LDC\LDCCRM\Models\Answer
65     *
66     * @var array
67     */
68    private $answers = [];
69
70    /**
71     * The crm customer fields
72     *
73     * @var array
74     */
75    protected $modelFields = [
76        'id', 'question', 'type', 'answers'
77    ];
78
79    /**
80     * Validation rules
81     *
82     * @var array
83     */
84    protected static $validation = [
85        'id' => 'required|int',
86        'question' => 'required|string|max:255',
87        'type' => ['required'],
88        'answers' => 'required|array'
89    ];
90
91    /**
92     * Question
93     *
94     * @param array $properties
95     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
96     */
97    public function __construct(array $properties = [])
98    {
99        parent::__construct();
100        $this->fill($properties);
101        return $this;
102    }
103
104    /**
105     * General accessor
106     *
107     * @param string $field
108     * @return mixed
109     */
110    public function __get(string $field)
111    {
112        $props = \array_diff(
113            $this->modelFields,
114            $this->invisibleFields
115        );
116        if (in_array($field, $props)) {
117            if ($field === 'type' and !\is_object($this->{$field})) {
118                return QuestionType::make($this->{$field});
119            }
120        }
121        return parent::__get($field);
122    }
123
124    /**
125     * Set prop on the model.
126     *
127     * @param string $field
128     * @param mixed  $value
129     * @return void
130     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
131     * @throws \LDC\LDCCRM\Exceptions\AccessError If trying to set a value on a unknown property.
132     */
133    public function __set(string $field, $value): void
134    {
135        if ($field === 'type') {
136            $value = (\is_object($value) and
137                get_class($value) === QuestionType::class) ? $value->value : $value;
138        }
139        parent::__set($field, $value);
140    }
141
142    /**
143     * Fill values into model using validation rules.
144     *
145     * @param array $properties
146     * @return void
147     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
148     */
149    public function fill(array $properties)
150    {
151        if (isset($properties['type'])) {
152            try {
153                $properties['type'] = (\is_object($properties['type']) and
154                    get_class($properties['type']) === QuestionType::class) ?
155                    $properties['type']->value : (ctype_digit($properties['type']) ?
156                        (int)$properties['type'] : $properties['type']);
157            } catch (TypeError | \BadMethodCallException $e) {
158                // * Cast anyway
159                $properties['type'] = (int)$properties['type'];
160            }
161        }
162        parent::fill($properties);
163    }
164
165    /**
166     * Validate all model properties
167     *
168     * @param array $ignore
169     * @return array the validated values
170     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field could not be validated.
171     */
172    protected function validateAllValues(array $ignore = []): array
173    {
174        $out = [];
175        if (!\in_array('type', $ignore)) {
176            try {
177                $this->validateValue('type', QuestionType::make(Utils::getRawProperty($this, 'type')));
178            } catch (TypeError | \BadMethodCallException $e) {
179                // Use this to throw validation error.
180                $this->validateValue('type', Utils::getRawProperty($this, 'type'));
181            }
182            $out['type'] = Utils::getRawProperty($this, 'type');
183        }
184        return array_merge($out, parent::validateAllValues(array_merge(['type'], $ignore)));
185    }
186
187    // * PUBLIC METHODS
188
189    /**
190     * Get all questions only
191     *
192     * @return array of \LDC\LDCCRM\Models\Question
193     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
194     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
195     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
196     */
197    public static function all(): array
198    {
199        static::setUpApiInterface();
200        $request = Utils::getResquest('apiV2.questions.index');
201
202        $response = static::$apiInterface->call($request);
203        $out      = [];
204        foreach ($response->data as $question) {
205            $answers             = \array_map(function ($answer) {
206                return new Answer($answer);
207            }, $question['answers']);
208            $question['answers'] = $answers;
209            $out[]               = new self($question);
210        }
211        return $out;
212    }
213
214    /**
215     * Get all the questions with customer answers
216     *
217     * @param \LDC\LDCCRM\Models\Customer $customer
218     * @return array
219     *
220     *   [
221     *        0 => [
222     *             'question' => \LDC\LDCCRM\Models\Question,
223     *             'customerAnswers' => \LDC\LDCCRM\Models\Answer[]
224     *        ]
225     *   ]
226     *
227     * with \LDC\LDCCRM\Models\Question[]
228     * and the customer \LDC\LDCCRM\Models\Answer[]
229     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a customer's hashcode could not be validated.
230     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
231     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
232     * Possibles are : - modelNotFound, If the customer could not be found(CRM side).
233     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
234     */
235    public static function allWithCustomerAnswers(Customer $customer): array
236    {
237        self::assertValidHascode($customer->hashcode);
238        static::setUpApiInterface();
239        $request = Utils::getResquest('apiV2.questions.indexWithResponse', [
240            'serviceCustomer' => $customer->hashcode
241        ]);
242
243        $response = static::$apiInterface->call($request);
244        $out      = [];
245        foreach ($response->data as $question) {
246            $answers         = \array_map(function ($answer) {
247                return new Answer($answer);
248            }, $question['answers']);
249            $customerAnswers = \array_map(function ($answer) {
250                return new Answer($answer);
251            }, $question['customerAnswers']);
252            unset($question['customerAnswers']);
253            $question['answers'] = $answers;
254            $out[]               = [
255                'question' => new self($question),
256                'customerAnswers' => $customerAnswers,
257            ];
258        }
259        return $out;
260    }
261
262    /**
263     * Get unanswered question for a specific customer
264     *
265     * @param \LDC\LDCCRM\Models\Customer $customer
266     * @return array of \LDC\LDCCRM\Models\Question
267     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a customer's hashcode could not be validated.
268     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
269     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
270     * Possibles are : - modelNotFound, If the customer could not be found(CRM side).
271     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
272     */
273    public static function unansweredBy(Customer $customer): array
274    {
275        self::assertValidHascode($customer->hashcode);
276        static::setUpApiInterface();
277        $request = Utils::getResquest(
278            'apiV2.questions.unAnsweredBy',
279            ['serviceCustomer' => $customer->hashcode]
280        );
281
282        $response = static::$apiInterface->call($request);
283        $out      = [];
284        foreach ($response->data as $question) {
285            $answers             = \array_map(function ($answer) {
286                return new Answer($answer);
287            }, $question['answers']);
288            $question['answers'] = $answers;
289            $out[]               = new self($question);
290        }
291        return $out;
292    }
293
294    /**
295     * Serialize this object to an array
296     *
297     * @return array
298     */
299    public function jsonSerialize()
300    {
301        return [
302            'id' => $this->id,
303            'question' => $this->question,
304            'type' => $this->type,
305            'answers' => $this->answers
306        ];
307    }
308}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Models/SingleOptin.php.html b/tests/coverage/clients/V2/src/Models/SingleOptin.php.html new file mode 100644 index 0000000..294aab8 --- /dev/null +++ b/tests/coverage/clients/V2/src/Models/SingleOptin.php.html @@ -0,0 +1,207 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Models/SingleOptin.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
SingleOptin
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 setStatus
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Models;
4
5use LDC\LDCCRM\Enums\ApiStatus;
6use LDC\LDCCRM\Utils;
7
8/**
9 * This class allows to handle single opt in
10 *
11 * @category Api Models
12 * @package LDCCrmClient
13 * @subpackage models
14 * @author Jérémy Munsch <jeremy.munsch@ldc.fr>
15 */
16abstract class SingleOptin extends CrmClientModel
17{
18    /**
19     * Validation rules
20     *
21     * @var array
22     */
23    protected static $validation = [
24        'email' => 'required|email',
25        'opt_in' => 'required|bool',
26    ];
27
28    /**
29     * Set the single opt in status for an email
30     *
31     * @param string  $email
32     * @param boolean $opt_in
33     * @return boolean true if succeeded
34     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field email could not be validated.
35     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
36     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
37     * Possibles are : - singleOptinCustomerExists, If a customer exists with this email.
38     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
39     */
40    public static function setStatus(string $email, bool $opt_in): bool
41    {
42        self::validateValue('email', $email);
43        static::setUpApiInterface();
44        $request = Utils::getResquest('apiV2.optin', [], [
45            'email' => $email,
46            'opt_in' => $opt_in
47        ]);
48
49        $response = static::$apiInterface->call($request);
50        return $response->status->equals(ApiStatus::OK());
51    }
52}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Models/Stats.php.html b/tests/coverage/clients/V2/src/Models/Stats.php.html new file mode 100644 index 0000000..8019dc0 --- /dev/null +++ b/tests/coverage/clients/V2/src/Models/Stats.php.html @@ -0,0 +1,263 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Models/Stats.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
Stats
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
2
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
 getBrandStats
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
 getQuestionStats
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Models;
4
5use LDC\LDCCRM\Utils;
6
7/**
8 * This class is used to get stats from CRM
9 *
10 * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If an error occurs
11 * @throws \LDC\LDCCRM\Exceptions\CrmException If the CRM encounters an error
12 *
13 * @category Api Models
14 * @package LDCCrmClient
15 * @subpackage models
16 * @author Jérémy Munsch <jeremy.munsch@ldc.fr>
17 */
18abstract class Stats extends CrmClientModel
19{
20    /**
21     * Validation rules
22     *
23     * @var array
24     */
25    protected static $validation = [
26        'question.id' => 'required|int|min:1',
27        'start' => 'required|date_format:Y-m-d',
28        'end' => 'required|date_format:Y-m-d'
29    ];
30
31    /**
32     * Get brand stats ordered by ascendent days
33     *
34     * @param string $start Une date valide au format Y-m-d .
35     * @param string $end   Une date valide au format Y-m-d .
36     * @return array The stats array by day
37     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field start could not be validated.
38     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field end could not be validated.
39     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
40     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
41     * Possibles are : - validationFailed, If provided data is wrong(CRM side).
42     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
43     */
44    public static function getBrandStats(string $start, string $end): array
45    {
46        self::validateValue('start', $start);
47        self::validateValue('end', $end);
48        static::setUpApiInterface();
49        $request  = Utils::getResquest('apiV2.stats.brandStats', [], [
50            'start' => $start,
51            'end' => $end
52        ]);
53        $response = static::$apiInterface->call($request);
54        return $response->data['stats'];
55    }
56
57    /**
58     * Get brand stats ordered by ascendent days
59     *
60     * @param \LDC\LDCCRM\Models\Question $question Une question CRM possèdant un id réel.
61     * @param string                      $start    Une date valide au format Y-m-d .
62     * @param string                      $end      Une date valide au format Y-m-d .
63     * @return array The stats array by day
64     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a question's id could not be validated.
65     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field start could not be validated.
66     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field end could not be validated.
67     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
68     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
69     * Possibles are : - validationFailed, If provided data is wrong(CRM side).
70     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
71     */
72    public static function getQuestionStats(Question $question, string $start, string $end): array
73    {
74        self::validateValue('question.id', $question->id);
75        self::validateValue('start', $start);
76        self::validateValue('end', $end);
77        static::setUpApiInterface();
78        $request  = Utils::getResquest('apiV2.stats.questionStats', [
79            'serviceQuestion' => $question->id,
80        ], [
81            'start' => $start,
82            'end' => $end
83        ]);
84        $response = static::$apiInterface->call($request);
85        return $response->data;
86    }
87}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Models/dashboard.html b/tests/coverage/clients/V2/src/Models/dashboard.html new file mode 100644 index 0000000..ccd7a99 --- /dev/null +++ b/tests/coverage/clients/V2/src/Models/dashboard.html @@ -0,0 +1,326 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/clients/V2/src/Models + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+
+

Project Risks

+ +
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodCoverage
validateAllValues0%
__set0%
findWithEmail0%
fill33%
fillRaw40%
fill52%
__set60%
validateAllValues71%
fill77%
fill77%
fill77%
validateAllValues77%
getPropType77%
assertValidHascode83%
assertValidPrimaryKey83%
validateValue85%
checkFieldType88%
+
+
+ +
+ +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Models/index.html b/tests/coverage/clients/V2/src/Models/index.html new file mode 100644 index 0000000..cb8851b --- /dev/null +++ b/tests/coverage/clients/V2/src/Models/index.html @@ -0,0 +1,318 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Models + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 87.32% covered (warning) +
+
+
87.32%
372 / 426
+
+ 64.81% covered (warning) +
+
+
64.81%
35 / 54
+
+ 25.00% covered (danger) +
+
+
25.00%
2 / 8
Answer.php
+
+ 94.44% covered (success) +
+
+
94.44%
17 / 18
+
+ 66.67% covered (warning) +
+
+
66.67%
2 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ArcadiaMailDefault.php
+
+ 92.86% covered (success) +
+
+
92.86%
52 / 56
+
+ 71.43% covered (warning) +
+
+
71.43%
5 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ArcadiaMailGame.php
+
+ 79.49% covered (warning) +
+
+
79.49%
31 / 39
+
+ 60.00% covered (warning) +
+
+
60.00%
3 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CrmClientModel.php
+
+ 78.79% covered (warning) +
+
+
78.79%
78 / 99
+
+ 41.67% covered (danger) +
+
+
41.67%
5 / 12
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
Customer.php
+
+ 94.59% covered (success) +
+
+
94.59%
105 / 111
+
+ 73.33% covered (warning) +
+
+
73.33%
11 / 15
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
Question.php
+
+ 82.05% covered (warning) +
+
+
82.05%
64 / 78
+
+ 66.67% covered (warning) +
+
+
66.67%
6 / 9
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
SingleOptin.php
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Stats.php
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/tests/coverage/clients/V2/src/Transport/ApiInterface.php.html b/tests/coverage/clients/V2/src/Transport/ApiInterface.php.html new file mode 100644 index 0000000..a06569e --- /dev/null +++ b/tests/coverage/clients/V2/src/Transport/ApiInterface.php.html @@ -0,0 +1,516 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Transport/ApiInterface.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 42.86% covered (danger) +
+
+
42.86%
3 / 7
CRAP
+
+ 87.50% covered (warning) +
+
+
87.50%
63 / 72
ApiInterface
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 42.86% covered (danger) +
+
+
42.86%
3 / 7
22.95
+
+ 87.50% covered (warning) +
+
+
87.50%
63 / 72
 __construct
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12.10
+
+ 60.00% covered (warning) +
+
+
60.00%
6 / 10
 call
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.04
+
+ 83.33% covered (warning) +
+
+
83.33%
5 / 6
 processRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
 setHeaders
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
 setCredentials
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.50
+
+ 50.00% covered (danger) +
+
+
50.00%
3 / 6
 setTimestamp
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 calulateSignature
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
5
+
+ 96.88% covered (success) +
+
+
96.88%
31 / 32
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Transport;
4
5use LDC\LDCCRM\Enums\ApiStatus;
6use LDC\LDCCRM\Exceptions\ApiErrorException;
7use LDC\LDCCRM\Exceptions\CrmException;
8use LDC\LDCCRM\Utils;
9
10/**
11 * @ignore
12 *
13 * This class contains static api credentials
14 * It handles a request to get a response
15 *
16 * @package LDCCrmClient
17 * @subpackage transport
18 */
19class ApiInterface
20{
21    /**
22     * The api endpoint url
23     * This is the CRM FQDN
24     *
25     * @var string
26     */
27    protected static $endpoint;
28
29    /**
30     * The bearer token
31     * This is related to a specific brand which allows
32     * the crm to identify the concerned brand
33     *
34     * @var string
35     */
36    protected static $bearerToken;
37
38    /**
39     * The bearer token password
40     *
41     * @var string
42     */
43    protected static $bearerPwd;
44
45    /**
46     * HTTP AUTH string
47     *
48     * @var string
49     */
50    protected static $authentification = null;
51
52    /**
53     * ApiInterface
54     *
55     * @param string|null $endpoint    The crm endpoint.
56     * @param string|null $bearerToken The crm service token.
57     * @param string|null $bearerPwd   The crm service password.
58     *
59     * @throws \LDC\LDCCRM\Exceptions\CrmException If crm.token, crm.password or
60     * crm.endpoint is missing on .env file or in laravel config.
61     */
62    public function __construct(string $endpoint = null, string $bearerToken = null, string $bearerPwd = null)
63    {
64        self::$endpoint    = self::$endpoint ?: $endpoint;
65        self::$bearerToken = self::$bearerToken ?: $bearerToken;
66        self::$bearerPwd   = self::$bearerPwd ?: $bearerPwd;
67
68        if (!self::$bearerToken or !self::$bearerPwd or !self::$endpoint) {
69            throw new CrmException(
70                'Either config or .env file need to have the token var set (crm.token, crm.password, crm.endpoint)'
71            );
72        }
73
74        // Handle HTTP Auth .
75        if (\strpos(self::$endpoint, '@')) {
76            $vars                   = \parse_url(self::$endpoint);
77            self::$authentification = \base64_encode($vars['user'] . ':' . $vars['pass']);
78        }
79    }
80
81    /**
82     * Make a crm http call to get a reponse
83     *
84     * @param \LDC\LDCCRM\Transport\Request $request
85     * @return \LDC\LDCCRM\Transport\Response
86     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
87     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
88     */
89    public function call(Request $request): Response
90    {
91        $response = $this->processRequest($request);
92
93        // * Handle response Status.
94        if ($response->status->equals(ApiStatus::KO())) {
95            if (\is_int($response->apiError)) {
96                throw new CrmException($response->message, $response->apiError);
97            } else {
98                throw new ApiErrorException($response->apiError, $response->httpCode, $response->message);
99            }
100        }
101        return $response;
102    }
103
104    /**
105     * Process request
106     *
107     * @param Request $request
108     * @return \LDC\LDCCRM\Transport\Response
109     */
110    private function processRequest(Request $request): Response
111    {
112        $this->setCredentials($request);
113        $this->setHeaders($request);
114        $this->setTimestamp($request);
115        $request->setEndpoint(self::$endpoint);
116        $request->addData([
117            'signature' => $this->calulateSignature($request)
118        ]);
119
120        /** @var \Psr\Http\Message\ResponseInterface $response */
121        $response = $request->call();
122        $status   = in_array($response->getStatusCode(), [200, 201]) ?
123            ApiStatus::OK() : ApiStatus::KO();
124
125        return new Response($status, $response);
126    }
127
128    /**
129     * Add spécific headers to he request
130     *
131     * @param \LDC\LDCCRM\Transport\Request $request
132     * @return void
133     */
134    private function setHeaders(Request &$request): void
135    {
136        $headers = [
137            'User-Agent' => sprintf('LDC CRM client version %s', Utils::getPackageInfo()['version']),
138            'Accept'     => 'application/json',
139            'Content-Type' => 'application/json'
140        ];
141        $request->addHeaders($headers);
142    }
143
144    /**
145     * Inject bearer credentials into the request
146     *
147     * @param \LDC\LDCCRM\Transport\Request $request
148     * @return void
149     */
150    protected function setCredentials(Request $request)
151    {
152        if (self::$authentification) {
153            $request->addHeaders([
154                'Authorization' => implode(',', [
155                    sprintf('Basic %s', self::$authentification)
156                ])
157            ]);
158        }
159        $request->addData(['bearer_token' => self::$bearerToken]);
160    }
161
162    /**
163     * Add a timestamp (UTC) to the request
164     *
165     * @param Request $request
166     * @return void
167     */
168    protected function setTimestamp(Request $request): void
169    {
170        $request->addData([
171            // ! UTC date time.
172            'timestamp' => \gmdate('Y-m-d H:i:s')
173        ]);
174    }
175
176    /**
177     * Calculates request signature
178     *
179     * @param Request $request
180     * @return string
181     * @throws \LDC\LDCCRM\Exceptions\CrmException In case of failure.
182     */
183    protected function calulateSignature(Request $request): string
184    {
185        $uri        = \parse_url(sprintf('%s/%s', self::$endpoint, $request->uri));
186        $method     = \strtolower($request->method->value);
187        $parameters = $request->data;
188
189        $signature = [
190            'uri' => sprintf(
191                '%s://%s%s',
192                // Assume https if non provided, security first.
193                $uri['scheme'] ?? 'https',
194                $uri['host'],
195                $uri['path'] ?? '',
196            ),
197            'method' => $method
198        ];
199
200        $flattenRequestArray = function (array $array) use (&$flattenRequestArray) {
201            $out = [];
202            foreach ($array as $key => $value) {
203                if (is_array($value)) {
204                    $subArray = $flattenRequestArray($value);
205                    foreach ($subArray as $subKey => $subValue) {
206                        $out["$key.$subKey"] = $subValue;
207                    }
208                    continue;
209                }
210                $out[$key] = $value;
211            }
212            return $out;
213        };
214
215        $parameters = $flattenRequestArray($parameters);
216
217        // ! Natural sort by keys.
218        if (!uksort($parameters, "strnatcmp")) {
219            throw new CrmException('Natural sorting the request parameters failed.');
220        }
221
222        $parameters = \array_map(function ($key, $value) {
223            return sprintf('%s=%s', \rawurlencode($key), \rawurlencode($value));
224        }, \array_keys($parameters), $parameters);
225
226        $signature[] = \implode('&', $parameters);
227
228        return base64_encode(hash_hmac(
229            'sha256',
230            \implode('\n', $signature),
231            self::$bearerPwd,
232            true
233        ));
234    }
235}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Transport/Request.php.html b/tests/coverage/clients/V2/src/Transport/Request.php.html new file mode 100644 index 0000000..847b55a --- /dev/null +++ b/tests/coverage/clients/V2/src/Transport/Request.php.html @@ -0,0 +1,477 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Transport/Request.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 71.43% covered (warning) +
+
+
71.43%
5 / 7
CRAP
+
+ 94.74% covered (success) +
+
+
94.74%
36 / 38
Request
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 71.43% covered (warning) +
+
+
71.43%
5 / 7
9.01
+
+ 94.74% covered (success) +
+
+
94.74%
36 / 38
 __construct
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
1.00
+
+ 91.67% covered (success) +
+
+
91.67%
11 / 12
 __get
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.15
+
+ 66.67% covered (warning) +
+
+
66.67%
2 / 3
 makeRequest
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
 setEndpoint
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 call
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 addHeaders
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 addData
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Transport;
4
5use GuzzleHttp\Client;
6use GuzzleHttp\Psr7\Request as Psr7Request;
7use GuzzleHttp\Psr7\Utils as Psr7Utils;
8use GuzzleHttp\RequestOptions;
9use GuzzleHttp\Utils as GuzzleHttpUtils;
10use LDC\LDCCRM\Enums\HttpMethods;
11use LDC\LDCCRM\Utils;
12use Psr\Http\Message\ResponseInterface;
13
14/**
15 * The LDC CRM request
16 * Contains the http method, the requested url and data to pass
17 *
18 * @property \LDC\LDCCRM\Enums\HttpMethods $method readonly method
19 * @property string $uri readonly uri
20 * @property array $headers headers
21 * @property array $data readonly data
22 *
23 * @package LDCCrmClient
24 * @subpackage transport
25 */
26class Request
27{
28    /**
29     * GuzzleHttp client
30     *
31     * @var \GuzzleHttp\Client
32     */
33    private $client;
34
35    /**
36     * The http method
37     *
38     * @var \LDC\LDCCRM\Enums\HttpMethods
39     */
40    protected $method;
41
42    /**
43     * The http url
44     *
45     * @var string
46     */
47    protected $uri;
48
49    /**
50     * Data to pass to the http headers
51     *
52     * @var array
53     */
54    protected $headers = [];
55
56    /**
57     * Data to pass to the http body
58     *
59     * @var array
60     */
61    protected $data = [];
62
63    /**
64     * Guzzle request
65     *
66     * @var callable the returns \GuzzleHttp\Psr7\Request
67     */
68    private $request;
69
70    /**
71     * The Api endpoint
72     *
73     * @var string
74     */
75    private $endpoint;
76
77    /**
78     * Request
79     *
80     * @param \LDC\LDCCRM\Enums\HttpMethods $method
81     * @param string                        $uri
82     * @param array                         $headers
83     * @param array                         $data
84     */
85    public function __construct(
86        HttpMethods $method,
87        string $uri,
88        array $headers = [],
89        array $data = []
90    ) {
91        $this->hidden  = function () {
92            return [];
93        };
94        $this->method  = $method;
95        $this->uri     = $uri;
96        $this->data    = $data;
97        $this->headers = $headers;
98        $this->client  = new Client([
99            RequestOptions::HEADERS => [
100                'User-Agent' => sprintf(
101                    'LDC CRM Client v%s',
102                    Utils::getPackageInfo()['version']
103                )
104            ]
105        ]);
106    }
107
108    /**
109     * Generic accessor
110     *
111     * @param string $field
112     * @return mixed|null
113     */
114    public function __get(string $field)
115    {
116        if (\in_array($field, ['method', 'uri', 'data'])) {
117            return $this->{$field};
118        }
119        return null;
120    }
121
122    /**
123     * make the guzzle request
124     *
125     * @return void
126     */
127    private function makeRequest(): void
128    {
129        $this->request = function () {
130            $uri = $this->uri;
131            if ($this->method->equals(HttpMethods::get())) {
132                $uri        = sprintf("%s?%s", $uri, http_build_query($this->data));
133                $this->data = [];
134            }
135            $request = new Psr7Request(
136                \strtoupper($this->method),
137                sprintf('%s/%s', $this->endpoint, $uri),
138                $this->headers
139            );
140            $request = Psr7Utils::modifyRequest($request, [
141                'body' => GuzzleHttpUtils::jsonEncode($this->data)
142            ]);
143            return $request;
144        };
145    }
146
147    /**
148     * Set the api endpoint to the request
149     *
150     * @param string $endpoint
151     * @return void
152     */
153    public function setEndpoint(string $endpoint): void
154    {
155        $this->endpoint = $endpoint;
156    }
157
158    /**
159     * Make the Http request
160     *
161     * @return ResponseInterface
162     */
163    public function call(): ResponseInterface
164    {
165        $this->makeRequest();
166        /** @var \GuzzleHttp\Psr7\Request */
167        $request = ($this->request)();
168        return $this->client->send($request, [
169            // ! Do not throw HTTP Exceptions.
170            'http_errors' => false,
171            'allow_redirects' => false
172        ]);
173    }
174
175    /**
176     * Add headers to the request
177     *
178     * @param array $headers
179     * @return void
180     */
181    public function addHeaders(array $headers)
182    {
183        $this->headers = \array_merge($this->headers, $headers);
184    }
185
186    /**
187     * Add data to the request
188     *
189     * @param array $data
190     * @return void
191     */
192    public function addData(array $data)
193    {
194        $this->data = \array_merge($this->data, $data);
195    }
196}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Transport/Response.php.html b/tests/coverage/clients/V2/src/Transport/Response.php.html new file mode 100644 index 0000000..675c26f --- /dev/null +++ b/tests/coverage/clients/V2/src/Transport/Response.php.html @@ -0,0 +1,419 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Transport/Response.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 25.00% covered (danger) +
+
+
25.00%
1 / 4
CRAP
+
+ 72.73% covered (warning) +
+
+
72.73%
32 / 44
Response
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 25.00% covered (danger) +
+
+
25.00%
1 / 4
22.86
+
+ 72.73% covered (warning) +
+
+
72.73%
32 / 44
 __construct
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
8.30
+
+ 83.33% covered (warning) +
+
+
83.33%
20 / 24
 __get
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
4.13
+
+ 80.00% covered (warning) +
+
+
80.00%
4 / 5
 tryDecodeJson
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
 checkVersion
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
4.41
+
+ 46.15% covered (danger) +
+
+
46.15%
6 / 13
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM\Transport;
4
5use LDC\LDCCRM\Enums\ApiError;
6use LDC\LDCCRM\Enums\ApiStatus;
7use LDC\LDCCRM\Enums\LogLevel;
8use LDC\LDCCRM\Exceptions\CrmException;
9use LDC\LDCCRM\Utils;
10use Psr\Http\Message\ResponseInterface;
11use RecursiveArrayIterator;
12use RecursiveIteratorIterator;
13
14/**
15 * @property \LDC\LDCCRM\Enums\ApiStatus $status The response api status
16 * @property \LDC\LDCCRM\Enums\ApiError|int $apiError The api error status it there is any
17 * @property int $httpCode The http response code
18 * @property string $message The api message
19 * @property array $headers The http reponse headers
20 * @property array $body The http response body, this is parsed json
21 * @property ?array $data The reponse data
22 *
23 * @package LDCCrmClient
24 * @subpackage transport
25 */
26class Response
27{
28    /**
29     * The response status
30     *
31     * @var \LDC\LDCCRM\Enums\ApiStatus
32     */
33    private $status;
34
35    /**
36     * The api error
37     *
38     * @var \LDC\LDCCRM\Enums\ApiError
39     */
40    private $apiError;
41
42    /**
43     * The response http code
44     *
45     * @var integer
46     */
47    private $httpCode = 0;
48
49    /**
50     * The response headers
51     *
52     * @var string
53     */
54    private $headers;
55
56    /**
57     * The response body
58     *
59     * @var string
60     */
61    private $body;
62
63    /**
64     * The interface message
65     *
66     * @var string
67     */
68    private $message;
69
70    /**
71     * PSR response
72     *
73     * @var \Psr\Http\Message\ResponseInterface
74     */
75    private $response;
76
77    /**
78     * Response
79     *
80     * @param \LDC\LDCCRM\Enums\ApiStatus         $status
81     * @param \Psr\Http\Message\ResponseInterface $response
82     * @throws \LDC\LDCCRM\Exceptions\CrmException If HTTP response body in not JSON.
83     */
84    public function __construct(ApiStatus $status, ResponseInterface $response)
85    {
86        $this->response = $response;
87        $this->status   = $status;
88        $this->httpCode = $response->getStatusCode();
89        $this->headers  = $response->getHeaders();
90        /** @var \GuzzleHttp\Psr7\Stream */
91        $this->body = $response->getBody();
92
93        if (!in_array($response->getStatusCode(), [200, 201])) {
94            $this->message = $response->getReasonPhrase();
95        }
96
97        // * Body should be json data.
98        if (!($decodedJson = $this->tryDecodeJson($this->body->getContents()))) {
99            if (Utils::getConfig('app.env') === 'testing') {
100                dump((string)$this->body);
101            }
102            throw new CrmException('The Api should have returned JSON data');
103        }
104        $this->body = $decodedJson;
105
106        // * If Api Error.
107        if ($this->status->equals(ApiStatus::KO())) {
108            // * If CRM Exception or Any Exception.
109            if ($this->httpCode === 500) {
110                $this->apiError = $this->body['data']['code'];
111            } else {
112                // * From here it should be an ApiErrorException.
113                $this->apiError = ApiError::make($this->body['data']['code']);
114                $this->message  = $this->body['data']['message'];
115                if ($this->apiError->equals(ApiError::validationFailed())) {
116                    $errors = new RecursiveIteratorIterator(
117                        new RecursiveArrayIterator($this->body['data']['errors'])
118                    );
119                    foreach ($errors as $error) {
120                        $this->message = "$this->message $error";
121                    }
122                }
123            }
124        }
125
126        $this->checkVersion($this->body['version']);
127
128        return $this;
129    }
130
131    /**
132     * Generic accessor
133     *
134     * @param string $field
135     * @return mixed|null
136     */
137    public function __get(string $field)
138    {
139        if (
140            \in_array($field, [
141            'status', 'httpCode', 'headers',
142            'body', 'message', 'response', 'apiError'
143            ])
144        ) {
145            return $this->{$field};
146        }
147        if ($field === 'data' and isset($this->body[$field])) {
148            return $this->body[$field];
149        }
150        return null;
151    }
152
153    /**
154     * Try to decode json and return false if it is
155     * not a valid json
156     *
157     * @param string $string
158     * @return array|false Returns an associative data array or false if failed to parse.
159     */
160    private function tryDecodeJson(string $string)
161    {
162        /** @var array|null */
163        $data = json_decode($string, true);
164        return (json_last_error() == JSON_ERROR_NONE) ? $data : false;
165    }
166
167    /**
168     * This checks if the crm version is way far beyond the client version.
169     * If a minor version mismatch is detected we will log this as a warning.
170     * If a major version mismatch is detected we will throw a CrmException as
171     * there shall be an incompatibility with the response.
172     *
173     * The goal here is to force clients to be up to date.
174     * A major version change means breaking change.
175     *
176     * @param string $version
177     * @return void
178     * @throws \LDC\LDCCRM\Exceptions\CrmException If a major version mismatch is detected.
179     */
180    private function checkVersion(string $version)
181    {
182        $packageInfo  = Utils::getPackageInfo();
183        $ApackageInfo = explode('.', $packageInfo['version']);
184        $Aversion     = \explode('.', $version);
185        if ($ApackageInfo[0] != $Aversion[0]) {
186            throw new CrmException(sprintf(
187                'Incompatible major versions between CRM server %s and client %s',
188                $version,
189                $packageInfo['version']
190            ));
191        }
192        if ($ApackageInfo[1] != $Aversion[1]) {
193            $message = sprintf(
194                'Minor version mismatch between CRM server %s and client %s, this could lead to errors',
195                $version,
196                $packageInfo['version']
197            );
198            Utils::log($message, LogLevel::WARNING());
199        }
200    }
201}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Transport/dashboard.html b/tests/coverage/clients/V2/src/Transport/dashboard.html new file mode 100644 index 0000000..ca4d015 --- /dev/null +++ b/tests/coverage/clients/V2/src/Transport/dashboard.html @@ -0,0 +1,305 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/clients/V2/src/Transport + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + +
ClassCoverage
LDC\LDCCRM\Transport\Response72%
LDC\LDCCRM\Transport\ApiInterface87%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + +
ClassCRAP
LDC\LDCCRM\Transport\ApiInterface22
LDC\LDCCRM\Transport\Response22
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + + + + + +
MethodCoverage
checkVersion46%
setCredentials50%
__construct60%
__get66%
__get80%
call83%
__construct83%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + + + + + + +
MethodCRAP
__construct12
__construct8
checkVersion4
__get4
call3
setCredentials2
__get2
+
+
+
+ +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/Transport/index.html b/tests/coverage/clients/V2/src/Transport/index.html new file mode 100644 index 0000000..7f3786e --- /dev/null +++ b/tests/coverage/clients/V2/src/Transport/index.html @@ -0,0 +1,178 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Transport + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 85.06% covered (warning) +
+
+
85.06%
131 / 154
+
+ 50.00% covered (danger) +
+
+
50.00%
9 / 18
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
ApiInterface.php
+
+ 87.50% covered (warning) +
+
+
87.50%
63 / 72
+
+ 42.86% covered (danger) +
+
+
42.86%
3 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
Request.php
+
+ 94.74% covered (success) +
+
+
94.74%
36 / 38
+
+ 71.43% covered (warning) +
+
+
71.43%
5 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
Response.php
+
+ 72.73% covered (warning) +
+
+
72.73%
32 / 44
+
+ 25.00% covered (danger) +
+
+
25.00%
1 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/tests/coverage/clients/V2/src/Utils.php.html b/tests/coverage/clients/V2/src/Utils.php.html new file mode 100644 index 0000000..b7df1bd --- /dev/null +++ b/tests/coverage/clients/V2/src/Utils.php.html @@ -0,0 +1,1373 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/Utils.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 61.54% covered (warning) +
+
+
61.54%
16 / 26
CRAP
+
+ 77.02% covered (warning) +
+
+
77.02%
191 / 248
Utils
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 61.54% covered (warning) +
+
+
61.54%
16 / 26
143.30
+
+ 77.02% covered (warning) +
+
+
77.02%
191 / 248
 makeValidator
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
7
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
 makeValidatorFromRule
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
10.90
+
+ 79.17% covered (warning) +
+
+
79.17%
19 / 24
 validationRequired
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 validationNullable
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.01
+
+ 88.89% covered (warning) +
+
+
88.89%
8 / 9
 validationSometimes
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
9 / 9
 validationInt
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 validationString
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 validationBool
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 validationMin
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.00
+
+ 92.31% covered (success) +
+
+
92.31%
12 / 13
 validationMax
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.11
+
+ 76.92% covered (warning) +
+
+
76.92%
10 / 13
 validationUuid
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 validationMixedCase
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 validationContainDigit
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 validationContainSymbol
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
 validationDate
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
 validationDateFormat
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
 validationEmail
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
 validationArray
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
 validationClosure
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 8
 getConfig
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
 getResquest
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
9.16
+
+ 87.50% covered (warning) +
+
+
87.50%
21 / 24
 getPackageInfo
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
 translate
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.21
+
+ 71.43% covered (warning) +
+
+
71.43%
5 / 7
 log
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
110
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 29
 setRawProperty
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
4.43
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
 getRawProperty
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.15
+
+ 66.67% covered (warning) +
+
+
66.67%
4 / 6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace LDC\LDCCRM;
4
5use LDC\LDCCRM\Enums\HttpMethods;
6use LDC\LDCCRM\Enums\LogLevel;
7use LDC\LDCCRM\Exceptions\AccessError;
8use LDC\LDCCRM\Exceptions\SkipValidationException;
9use LDC\LDCCRM\Exceptions\ValidationException;
10use LDC\LDCCRM\Transport\Request;
11use Respect\Validation\Validator;
12
13/**
14 * This is the LDCCRM utility class
15 *
16 * @category Utility
17 * @uses \LDC\LDCCRM\Enums\HttpMethods
18 * @uses \LDC\LDCCRM\Transport\Request
19 *
20 * @package LDCCrmClient
21 * @subpackage utilities
22 */
23class Utils
24{
25    /**
26     * Package infos
27     *
28     * @var array
29     */
30    private static $packageInfos;
31
32    /**
33     * Loaded translations
34     *
35     * @var array
36     */
37    private static $translations = [];
38
39    /**
40     * Create a new Validator instance.
41     *
42     * @param array $data
43     * @param array $rules
44     * @return closure
45     * @throws \ErrorException If an unknown validation rule is used.
46     */
47    public static function makeValidator(
48        array $data,
49        array $rules
50    ): object {
51        foreach ($rules as $attribute => $attrRules) {
52            $validators = [];
53            if (\is_string($attrRules)) {
54                $attrRules = \explode('|', $attrRules);
55            }
56            foreach ($attrRules as $rule) {
57                $validators[] = self::makeValidatorFromRule($rule, $data, $attribute);
58            } //end foreach
59        } //end foreach
60        return function () use ($validators) {
61            foreach ($validators as $validator) {
62                try {
63                    if (!$validator['validate']()) {
64                        throw new ValidationException($validator['error']);
65                    }
66                } catch (SkipValidationException $e) {
67                    break;
68                }
69            }
70        };
71    }
72
73    /**
74     * Generate a validator
75     *
76     * @param mixed  $rule
77     * @param array  $data
78     * @param string $attribute
79     * @return array
80     * @throws \ErrorException If a rule is unknown.
81     */
82    private static function makeValidatorFromRule(
83        $rule,
84        array $data,
85        string $attribute
86    ): array {
87        $rParams = [];
88        $closure = null;
89        if (\is_callable($rule) and !is_string($rule)) {
90            /** @var \Closure $closure */
91            $closure = $rule;
92            $rule    = '#closure#';
93        } elseif (\strpos($rule, ':') !== false) {
94            $rParams = \explode(':', $rule, 2);
95            $rule    = \array_shift($rParams);
96        }
97        switch (true) {
98            case in_array($rule, [
99                'nullable', 'sometimes',
100                'int', 'bool', 'uuid', 'string',
101                'date', 'email', 'array',
102                'mixedCase', 'containDigit', 'containSymbol',
103            ]):
104                return self::{sprintf('validation%s', \ucfirst($rule))}(
105                    $attribute,
106                    $data[$attribute] ?? null
107                );
108            case in_array($rule, ['min', 'max']):
109                return self::{sprintf('validation%s', \ucfirst($rule))}(
110                    $attribute,
111                    $data[$attribute],
112                    $rParams
113                );
114            case in_array($rule, ['required', 'sometimes']):
115                return self::{sprintf('validation%s', \ucfirst($rule))}(
116                    $attribute,
117                    $data,
118                );
119            case $rule === 'date_format':
120                return self::validationDateFormat($attribute, $data[$attribute], $rParams);
121            case $rule === '#closure#':
122                /** @var \Closure $rule */
123                return self::validationClosure($attribute, $data[$attribute], $closure);
124            default:
125                throw new \ErrorException("Unknown validation rule $rule " . implode(': ', $rParams));
126        } //end switch
127    }
128
129    // * VALIDATION RULES
130
131    /**
132     * Make Required validator
133     *
134     * @param string $attribute
135     * @param array  $data
136     * @return array
137     */
138    private static function validationRequired(string $attribute, array $data): array
139    {
140        return [
141            'type' => 'required',
142            'error' => sprintf(
143                '%s est obligatoire',
144                self::translate($attribute)
145            ),
146            'validate' => function () use ($attribute, $data) {
147                return isset($data[$attribute]);
148            }
149        ];
150    }
151
152    /**
153     * Make Nullable validator
154     *
155     * @param string $attribute
156     * @param mixed  $value
157     * @return array
158     */
159    private static function validationNullable(string $attribute, $value): array
160    {
161        return [
162            'type' => 'nullable',
163            'error' => sprintf(
164                '%s peut être égale à nul',
165                self::translate($attribute)
166            ),
167            'validate' => function () use ($value) {
168                if (is_null($value)) {
169                    throw new SkipValidationException();
170                }
171                return true;
172            }
173        ];
174    }
175
176    /**
177     * Make Sometimes validator
178     *
179     * @param string $attribute
180     * @param mixed  $data
181     * @return array
182     */
183    private static function validationSometimes(string $attribute, $data): array
184    {
185        return [
186            'type' => 'sometimes',
187            'error' => sprintf(
188                '%s est falcutatif',
189                self::translate($attribute)
190            ),
191            'validate' => function () use ($data) {
192                if (empty($data)) {
193                    throw new SkipValidationException();
194                }
195                return true;
196            }
197        ];
198    }
199
200    /**
201     * Make Int validator
202     *
203     * @param string $attribute
204     * @param mixed  $value
205     * @return array
206     */
207    private static function validationInt(string $attribute, $value): array
208    {
209        return [
210            'type' => 'int',
211            'error' => sprintf(
212                '%s doit être un entier',
213                self::translate($attribute)
214            ),
215            'validate' => function () use ($value) {
216                return \is_int($value);
217            }
218        ];
219    }
220
221    /**
222     * Make String validator
223     *
224     * @param string $attribute
225     * @param mixed  $value
226     * @return array
227     */
228    private static function validationString(string $attribute, $value): array
229    {
230        return [
231            'type' => 'string',
232            'error' => sprintf(
233                '%s doit être une chaine de charactères',
234                self::translate($attribute)
235            ),
236            'validate' => function () use ($value) {
237                return \is_string($value);
238            }
239        ];
240    }
241
242    /**
243     * Make Bool validator
244     *
245     * @param string $attribute
246     * @param mixed  $value
247     * @return array
248     */
249    private static function validationBool(string $attribute, $value): array
250    {
251        return [
252            'type' => 'bool',
253            'error' => sprintf(
254                '%s doit être un booléen',
255                self::translate($attribute)
256            ),
257            'validate' => function () use ($value) {
258                return \is_bool($value) or $value === 1 or $value === 0;
259            }
260        ];
261    }
262
263    /**
264     * Make Min validator
265     *
266     * @param string $attribute
267     * @param mixed  $value
268     * @param array  $params
269     * @return array
270     */
271    private static function validationMin(string $attribute, $value, array $params): array
272    {
273        return [
274            'type' => 'min',
275            'error' => sprintf(
276                '%s doit avoir au moins %s caratères, ou être plus grand que %s si c\'est un nombre',
277                self::translate($attribute),
278                $params[0],
279                $params[0]
280            ),
281            'validate' => function () use ($value, $params) {
282                if (\is_string($value)) {
283                    return \strlen($value) >= (int)$params[0];
284                }
285                if (\is_int($value)) {
286                    return $value >= (int)$params[0];
287                }
288                return false;
289            }
290        ];
291    }
292
293    /**
294     * Make Max validator
295     *
296     * @param string $attribute
297     * @param mixed  $value
298     * @param array  $params
299     * @return array
300     */
301    private static function validationMax(string $attribute, $value, array $params): array
302    {
303        return [
304            'type' => 'max',
305            'error' => sprintf(
306                '%s doit avoir au moins %s caratères, ou être plus grand que %s si c\'est un nombre',
307                self::translate($attribute),
308                $params[0],
309                $params[0]
310            ),
311            'validate' => function () use ($value, $params) {
312                if (\is_string($value)) {
313                    return \strlen($value) <= (int)$params[0];
314                }
315                if (\is_int($value)) {
316                    return $value <= (int)$params[0];
317                }
318                return false;
319            }
320        ];
321    }
322
323    /**
324     * Make Uuid validator
325     *
326     * @param string $attribute
327     * @param mixed  $value
328     * @return array
329     */
330    private static function validationUuid(string $attribute, $value): array
331    {
332        return [
333            'type' => 'uuid',
334            'error' => sprintf(
335                '%s doit être un uuid valide',
336                self::translate($attribute)
337            ),
338            'validate' => function () use ($value) {
339                return Validator::uuid()->validate($value);
340            }
341        ];
342    }
343
344    /**
345     * Make MixedCase validator
346     *
347     * @param string $attribute
348     * @param mixed  $value
349     * @return array
350     */
351    private static function validationMixedCase(string $attribute, $value): array
352    {
353        return [
354            'type' => 'mixedCase',
355            'error' => sprintf(
356                '%s doit contenir des lettre minuscules et majuscules',
357                self::translate($attribute)
358            ),
359            'validate' => function () use ($value) {
360                return \preg_match('/[a-z]/', $value) and \preg_match('/[A-Z]/', $value);
361            }
362        ];
363    }
364
365    /**
366     * Make ContainDigit validator
367     *
368     * @param string $attribute
369     * @param mixed  $value
370     * @return array
371     */
372    private static function validationContainDigit(string $attribute, $value): array
373    {
374        return [
375            'type' => 'containDigit',
376            'error' => sprintf(
377                '%s doit contenir au moins un chiffre',
378                self::translate($attribute)
379            ),
380            'validate' => function () use ($value) {
381                return \preg_match('/[0-9]/', $value);
382            }
383        ];
384    }
385
386    /**
387     * Make ContainSymbol validator
388     *
389     * @param string $attribute
390     * @param mixed  $value
391     * @return array
392     */
393    private static function validationContainSymbol(string $attribute, $value): array
394    {
395        return [
396            'type' => 'containSymbol',
397            'error' => sprintf(
398                '%s doit contenir au moins un caractère spécial comme une virgule par exemple',
399                self::translate($attribute)
400            ),
401            'validate' => function () use ($value) {
402                return \preg_match('/[^a-zA-Z0-9]/', $value);
403            }
404        ];
405    }
406
407    /**
408     * Make Date validator
409     *
410     * @param string $attribute
411     * @param mixed  $value
412     * @return array
413     */
414    private static function validationDate(string $attribute, $value): array
415    {
416        return [
417            'type' => 'date',
418            'error' => sprintf('%s doit être une date valide', self::translate($attribute)),
419            'validate' => function () use ($value) {
420                return Validator::dateTime()->validate($value);
421            }
422        ];
423    }
424
425    /**
426     * Make DateFormat validator
427     *
428     * @param string $attribute
429     * @param mixed  $value
430     * @param array  $params
431     * @return array
432     */
433    private static function validationDateFormat(string $attribute, $value, array $params): array
434    {
435        return [
436            'type' => 'date_format',
437            'error' => sprintf('%s doit être une date au format %s', self::translate($attribute), $params[0]),
438            'validate' => function () use ($value, $params) {
439                return Validator::dateTime($params[0])->validate($value);
440            }
441        ];
442    }
443
444    /**
445     * Make Email validator
446     *
447     * @param string $attribute
448     * @param mixed  $value
449     * @return array
450     */
451    private static function validationEmail(string $attribute, $value): array
452    {
453        return [
454            'type' => 'email',
455            'error' => sprintf('%s doit être un email valide', self::translate($attribute)),
456            'validate' => function () use ($value) {
457                return Validator::email()->validate($value);
458            }
459        ];
460    }
461
462    /**
463     * Make Array validator
464     *
465     * @param string $attribute
466     * @param mixed  $value
467     * @return array
468     */
469    private static function validationArray(string $attribute, $value): array
470    {
471        return [
472            'type' => 'array',
473            'error' => sprintf('%s doit être un tableau valide', self::translate($attribute)),
474            'validate' => function () use ($value) {
475                return Validator::arrayType()->validate($value);
476            }
477        ];
478    }
479
480    /**
481     * Make Closure validator
482     *
483     * @param string   $attribute
484     * @param mixed    $value
485     * @param \Closure $closure
486     * @return array
487     */
488    private static function validationClosure(string $attribute, $value, \Closure $closure): array
489    {
490        return [
491            'type' => 'closure',
492            'error' => sprintf('%s doit être validé', self::translate($attribute)),
493            'validate' => function () use ($attribute, $value, $closure) {
494                $closure($attribute, $value, function ($message) {
495                    throw new ValidationException($message);
496                });
497                return true;
498            }
499        ];
500    }
501
502    // * END VALIDATION RULES
503
504    /**
505     * This is only used for unit tests.
506     * Get config
507     *
508     * @param string $key
509     * @param string $default
510     * @return string|null
511     */
512    public static function getConfig(string $key, string $default = null): ?string
513    {
514        $value = null;
515        if (function_exists('config')) {
516            $value = \config($key, $default);
517        }
518
519        return $value;
520    }
521
522    /**
523     * Generate a Request from the route name
524     *
525     * @param string $routeName
526     * @param array  $query     Params to pass on to the route.
527     * @param array  $data      Http body params.
528     * @return \LDC\LDCCRM\Transport\Request
529     * @throws \ErrorException If the requested route is unknown.
530     * @throws \ErrorException If a required parameter is missing.
531     */
532    public static function getResquest(string $routeName, array $query = [], array $data = []): Request
533    {
534        // Convert bool to integer.
535        $query = \array_map(function ($v) {
536            return is_bool($v) ? (int)$v : $v;
537        }, $query);
538        $data  = \array_map(function ($v) {
539            return is_bool($v) ? (int)$v : $v;
540        }, $data);
541
542        $routes = \json_decode(\file_get_contents(__DIR__ . '/routes.json'), true);
543        if (!isset($routes[$routeName])) {
544            throw new \ErrorException("Unknown route $routeName from the crm");
545        }
546        $route        = $routes[$routeName];
547        $matches      = [];
548        $matchedIndex = [];
549        \preg_match_all('/{([\w-]*)}/', $route['uri'], $matches);
550        if (\count($matches[0])) {
551            foreach ($matches[0] as $k => $match) {
552                if (!isset($query[$matches[1][$k]])) {
553                    throw new \ErrorException(sprintf('Missing required parameter %s', $matches[1][$k]));
554                }
555                $route['uri']                  = str_replace($match, $query[$matches[1][$k]], $route['uri']);
556                $matchedIndex[$matches[1][$k]] = $query[$matches[1][$k]];
557            }
558        }
559        foreach ($matchedIndex as $key => $value) {
560            unset($query[$key]);
561        }
562        if (count($query)) {
563            $route['uri'] = sprintf('%s?%s', $route['uri'], \http_build_query($query));
564        }
565        return new Request(HttpMethods::make($route['methods'][0]), $route['uri'], [], $data);
566    }
567
568    /**
569     * Get composer.json infos
570     *
571     * @return array
572     */
573    public static function getPackageInfo(): array
574    {
575        if (!static::$packageInfos) {
576            static::$packageInfos = \json_decode(\file_get_contents(__DIR__ . '/../composer.json'), true);
577        }
578        return static::$packageInfos;
579    }
580
581    /**
582     * Translation a string using php file lang
583     *
584     * @param string $key
585     * @param string $lang
586     * @return string
587     */
588    public static function translate(string $key, string $lang = 'fr'): string
589    {
590        try {
591            if (!array_key_exists($lang, self::$translations)) {
592                self::$translations = require __DIR__ . "/../resources/lang/$lang.php";
593            }
594        } finally {
595            if (!isset(self::$translations['validation'][$key])) {
596                return $key;
597            } else {
598                return self::$translations['validation'][$key];
599            }
600        }
601    }
602
603    /**
604     * Write log using syslog
605     * It also write log using \Illuminate\Support\Facades\Log
606     * if it is present
607     *
608     * @param string                     $message The message is prefixed with "LDC CRM:" .
609     * @param \LDC\LDCCRM\Enums\LogLevel $level   The log level to apply.
610     * @return void
611     */
612    public static function log(string $message, LogLevel $level)
613    {
614        $message = "LDC CRM: $message";
615        if (\class_exists('\Illuminate\Support\Facades\Log')) {
616            switch ($level) {
617                case LogLevel::EMERG():
618                    \Illuminate\Support\Facades\Log::emergency($message);
619                    break;
620                case LogLevel::ALERT():
621                    \Illuminate\Support\Facades\Log::alert($message);
622                    break;
623                case LogLevel::CRIT():
624                    \Illuminate\Support\Facades\Log::critical($message);
625                    break;
626                case LogLevel::ERR():
627                    \Illuminate\Support\Facades\Log::error($message);
628                    break;
629                case LogLevel::WARNING():
630                    \Illuminate\Support\Facades\Log::warning($message);
631                    break;
632                case LogLevel::NOTICE():
633                    \Illuminate\Support\Facades\Log::notice($message);
634                    break;
635                case LogLevel::INFO():
636                    \Illuminate\Support\Facades\Log::info($message);
637                    break;
638                case LogLevel::DEBUG():
639                    \Illuminate\Support\Facades\Log::debug($message);
640                    break;
641            }//end switch
642            \Illuminate\Support\Facades\Log::warning($message);
643        }//end if
644        \syslog(\constant($level), $message);
645    }
646
647    /**
648     * Set a property without any validation.
649     *
650     * ! Warning : this can change any property of the object, use with caution.
651     *
652     * @param object $object   The object to inspect.
653     * @param string $property The object property to assign to.
654     * @param mixed  $value    The value to insert.
655     * @return void
656     * @throws LDC\LDCCRM\Exceptions\AccessError If trying to set a value on a unknown property.
657     */
658    public static function setRawProperty(object $object, string $property, $value): void
659    {
660        if (is_object($value) and in_array(BaseEnum::class, class_parents($value))) {
661            $value = $value->value;
662        }
663        $objectName = \get_class($object);
664        $refClass   = new \ReflectionClass($objectName);
665        try {
666            $refProp = $refClass->getProperty($property);
667        } catch (\ReflectionException $e) {
668            throw new AccessError($objectName, $property);
669        }
670        $refProp->setAccessible(true);
671        $refProp->setValue($object, $value);
672    }
673
674    /**
675     * Get a property of an object.
676     *
677     * ! Warning : this will return any property of the object, use with caution.
678     *
679     * @param object $object   The object to inspect.
680     * @param string $property The object property to inspect.
681     * @return mixed|null Returns null if the property was not found any way.
682     */
683    public static function getRawProperty(object $object, string $property)
684    {
685        $refClass = new \ReflectionClass(\get_class($object));
686        try {
687            $refProp = $refClass->getProperty($property);
688        } catch (\ReflectionException $e) {
689            return null;
690        }
691        $refProp->setAccessible(true);
692        return $refProp->getValue($object);
693    }
694}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/WordPress/WPCrm.php.html b/tests/coverage/clients/V2/src/WordPress/WPCrm.php.html new file mode 100644 index 0000000..8c6bd0e --- /dev/null +++ b/tests/coverage/clients/V2/src/WordPress/WPCrm.php.html @@ -0,0 +1,1269 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/WordPress/WPCrm.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 25
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 155
WPCrm
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 25
6006
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 155
 getUser
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
 getUserFromApi
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
30
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 11
 login
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
 logout
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
 check
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
 saveUser
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
56
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 11
 deleteUser
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
72
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 12
 toggleUserOptin
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
72
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 12
 setSingleOptin
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
 activateUser
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
30
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 8
 getNewUser
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
 unsetActiveUser
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
 loginUserWithApi
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
 getCustomer
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
 setUser
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
 initLdcApi
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
 makeApiV2Call
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
30
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 11
 bootSessionIfNeeded
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
156
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 17
 getUserFromTable
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
 getUserFromTableByEmail
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
 getUserFromTableByUuid
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
 saveOrUpdateUserToTable
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 11
 deleteUserFromTable
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
 query
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
 getWpDb
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3/**
4 * LDCCRM For Wordpress usage
5 *
6 * @category API
7 * @package  None
8 * @author   Jérémy Munsch <jeremy.munsch@ldc.fr>
9 * @license  MIT https://opensource.org/licenses/MIT
10 * @link     /
11 */
12
13namespace LDC\LDCCRM\WordPress;
14
15use LDC\LDCCRM\CrmApiV2;
16use LDC\LDCCRM\Enums\ApiError;
17use LDC\LDCCRM\Enums\LogLevel;
18use LDC\LDCCRM\Exceptions\ApiErrorException;
19use LDC\LDCCRM\Exceptions\CrmException;
20use LDC\LDCCRM\Models\Customer;
21use LDC\LDCCRM\Models\SingleOptin;
22use LDC\LDCCRM\Utils;
23
24/* phpcs:ignore Squiz.PHP.CommentedOutCode.Found
25    TABLE LDC CUSTOMERS
26    CREATE TABLE `ldc_wp_customers` (
27    `hashcode` VARCHAR(36) NOT NULL DEFAULT '' COMMENT 'Le hashcode (UUID)',
28    `email` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Le customer email',
29    `uuid` VARCHAR(50) NOT NULL DEFAULT '' COMMENT 'Code unique (inscripton, validation, etc)',
30    PRIMARY KEY (`hashcode`)
31    )
32    COMMENT='Les customers sur un WP liées à la CRM'
33    COLLATE='utf8mb4_unicode_ci';
34*/
35
36/**
37 * Class de gestion des users en session avec Wordpress (ce truc immonde)
38 *
39 * @category API
40 * @package LDCCrmClient
41 * @subpackage Wordpress
42 * @author   Jérémy Munsch <jeremy.munsch@ldc.fr>
43 * @license  MIT https://opensource.org/licenses/MIT
44 * @link     /
45 */
46class WPCrm
47{
48    /**
49     * The session key name
50     *
51     * @var string
52     */
53    private static $sessionKey = null;
54
55    /**
56     * The session live status
57     *
58     * @var boolean
59     */
60    private static $sessionDestroyed = false;
61
62    /**
63     * The user object
64     *
65     * @var \LDC\LDCCRM\CrmApiV2
66     */
67    protected static $api = null;
68
69    /**
70     * The user object
71     *
72     * @var \LDC\LDCCRM\WordPress\WPCrmUser
73     */
74    protected static $user = null;
75
76    /**
77     * Get the current user
78     * return null if the user is not logged in
79     *
80     * @return \LDC\LDCCRM\WordPress\WPCrmUser|null
81     */
82    public static function getUser()
83    {
84        self::bootSessionIfNeeded();
85        return self::$user;
86    }
87
88    /**
89     * Get the current user
90     * return api response code if an error occurs
91     *
92     * @param string $identification The customer hashcode or email.
93     *
94     * @return \LDC\LDCCRM\Wordpress\WPCrmUser|false
95     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
96     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
97     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
98     */
99    public static function getUserFromApi(string $identification)
100    {
101        self::initLdcApi();
102        try {
103            if (!($response = self::makeApiV2Call(self::$api, 'getCustomer', [$identification]))) {
104                return false;
105            }
106        } catch (ApiErrorException $e) {
107            switch ($e->getApiError()) {
108                case ApiError::modelNotFound():
109                    return false;
110                default:
111                    throw $e;
112            }
113        }
114        /** @var \LDC\LDCCRM\Models\Customer */
115        $customer = $response;
116        self::setUser($customer);
117        return self::$user;
118    }
119
120    /**
121     * Try to login with CRM API
122     * A session will be started if credentials are valid.
123     *
124     * @param string $email    The customer email.
125     * @param string $password The customer password.
126     *
127     * @return \LDC\LDCCRM\WordPress\WPCrmUser|false
128     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
129     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
130     * Possibles are : - authFailed, If credentials are not valid.
131     *                 - customerNotActivated, If the account is not activated.
132     */
133    public static function login(string $email, string $password)
134    {
135        self::bootSessionIfNeeded();
136        $validCredentials = self::loginUserWithApi($email, $password);
137
138        if ($validCredentials and ($user = self::getUserFromApi($email))) {
139            self::saveOrUpdateUserToTable();
140            $_SESSION['wpcrm'][self::$sessionKey] = json_encode($user);
141        }
142
143        return $validCredentials;
144    }
145
146    /**
147     * Destroy the customer from session
148     * and therefore logout
149     *
150     * @return void
151     */
152    public static function logout()
153    {
154        self::bootSessionIfNeeded();
155        $_SESSION['wpcrm'] = [];
156        session_destroy();
157        self::$sessionDestroyed = true;
158    }
159
160    /**
161     * Check if the user is logged in.
162     * This means he has an active valid session.
163     *
164     * @return boolean
165     */
166    public static function check()
167    {
168        self::bootSessionIfNeeded();
169        return (bool)self::getUser();
170    }
171
172    /**
173     * Save current customer to
174     * the local database and on crm
175     *
176     * @return boolean true if the operation succeeded false otherwise
177     * @throws LDC\LDCCRM\Exceptions\CrmException If a critical error occured.
178     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
179     * Possibles are : - validationFailed, If provided data is wrong(CRM side).
180     *                 - modelNotFound, If this customer hashcode could not be found.
181     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
182     */
183    public static function saveUser(): bool
184    {
185        if (!self::getUser() or !($customer = self::getCustomer())) {
186            return false;
187        }
188        try {
189            if (!self::makeApiV2Call($customer, 'save')) {
190                return false;
191            }
192        } catch (ApiErrorException $e) {
193            switch ($e->getApiError()) {
194                case ApiError::modelNotFound():
195                    return false;
196                default:
197                    throw $e;
198            }
199        }
200        self::saveOrUpdateUserToTable();
201        return true;
202    }
203
204    /**
205     * Delete current customer from
206     * the local database and the crm
207     *
208     * @return boolean true if the operation succeeded
209     * @throws LDC\LDCCRM\Exceptions\CrmException If a critical error occured.
210     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
211     * Possibles are : - modelNotFound, If this customer hashcode could not be found.
212     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
213     */
214    public static function deleteUser(): bool
215    {
216        if (!self::getUser() or !($customer = self::getCustomer())) {
217            return false;
218        }
219        try {
220            if (!self::makeApiV2Call($customer, 'delete')) {
221                return false;
222            }
223        } catch (ApiErrorException $e) {
224            switch ($e->getApiError()) {
225                case ApiError::authorizationException():
226                case ApiError::modelNotFound():
227                    return false;
228                default:
229                    throw $e;
230            }
231        }
232        self::deleteUserFromTable();
233        return true;
234    }
235
236    /**
237     * Toggle the current user optin value
238     * on the current user and the crm
239     *
240     * @return boolean true if the operation succeeded
241     * @throws LDC\LDCCRM\Exceptions\CrmException If a critical error occured.
242     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
243     * Possibles are : - modelNotFound, If this customer hashcode could not be found.
244     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
245     */
246    public static function toggleUserOptin(): bool
247    {
248        if (!self::getUser() or !($customer = self::getCustomer())) {
249            return false;
250        }
251        try {
252            if (!self::makeApiV2Call($customer, 'save')) {
253                return false;
254            }
255        } catch (ApiErrorException $e) {
256            switch ($e->getApiError()) {
257                case ApiError::authorizationException():
258                case ApiError::modelNotFound():
259                    return false;
260                default:
261                    throw $e;
262            }
263        }
264        $customer->opt_in = !$customer->opt_in;
265        return true;
266    }
267
268    /**
269     * Set the single optin status for an email
270     * on the crm
271     *
272     * @param string  $email
273     * @param boolean $opt_in
274     *
275     * @return boolean true if the operation succeeded false otherwise
276     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a field email could not be validated.
277     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
278     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
279     * Possibles are : - singleOptinCustomerExists, If a customer exists with this email.
280     */
281    public static function setSingleOptin(string $email, bool $opt_in): bool
282    {
283        return self::makeApiV2Call(SingleOptin::class, 'setStatus', [
284            $email, $opt_in
285        ]);
286    }
287
288    /**
289     * Toggle the current user optin value
290     * on the current user and the crm
291     *
292     * @return boolean True if the operation succeeded.
293     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a customer's hashcode could not be validated.
294     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
295     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
296     * Possibles are : - modelNotFound, If this customer hashcode could not be found.
297     *                 - customerAlreadyActive, It the customer is already activated.
298     */
299    public static function activateUser(): bool
300    {
301        if (!self::getUser() or !($customer = self::getCustomer())) {
302            return false;
303        }
304        if ($customer->status) {
305            return true;
306        }
307        if (!self::makeApiV2Call($customer, 'activate')) {
308            return false;
309        }
310        $customer->opt_in = !$customer->opt_in;
311        return true;
312    }
313
314    /**
315     * Generate a new user
316     *
317     * @return WPCrmUser
318     */
319    public static function getNewUser(): WPCrmUser
320    {
321        self::$user = new WPCrmUser();
322        return self::$user;
323    }
324
325    /**
326     * Remove active user
327     * to prevent WPCrm::check to return true
328     *
329     * @return void
330     */
331    public static function unsetActiveUser(): void
332    {
333        self::$user = null;
334    }
335
336    // * --- PRIVATE --- METHODS
337
338    /**
339     * Check customer credentials using the CRM.
340     *
341     * @param string $email    The customer email.
342     * @param string $password The customer password.
343     *
344     * @return boolean
345     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
346     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
347     * Possibles are : - authFailed, If credentials are not valid.
348     *                 - customerNotActivated, If the account is not activated.
349     */
350    private static function loginUserWithApi(string $email, string $password): bool
351    {
352        self::initLdcApi();
353
354        /** @var boolean $response */
355        $response = self::makeApiV2Call(self::$api, 'login', [$email, $password]);
356        return $response;
357    }
358
359    /**
360     * Get all customer infos
361     *
362     * @return \LDC\LDCCRM\Models\Customer|null
363     */
364    private static function getCustomer(): ?Customer
365    {
366        if (!self::getUser()) {
367            return null;
368        }
369        if (!Utils::getRawProperty(self::$user, 'info')) {
370            self::getUserFromApi(self::$user->hashcode);
371        }
372        /** @var \LDC\LDCCRM\Models\Customer */
373        $customer = Utils::getRawProperty(self::$user, 'info');
374        return $customer;
375    }
376
377    /**
378     * Create a user
379     * stores it to the session
380     * and finally saves it to the database if it does not exists
381     *
382     * @param \LDC\LDCCRM\Models\Customer $customer
383     *
384     * @return void
385     */
386    private static function setUser(Customer $customer): void
387    {
388        self::bootSessionIfNeeded();
389        if (!self::$user) {
390            self::$user = new WPCrmUser($customer->hashcode, $customer->email);
391        }
392        self::$user->setInfo($customer);
393    }
394
395    /**
396     * Get the initialized API object
397     *
398     * @return \LDC\LDCCRM\CrmApiV2
399     * @throws \ErrorException If php does not have extension curl.
400     */
401    private static function initLdcApi(): CrmApiV2
402    {
403        if (!self::$api) {
404            if (!extension_loaded('curl')) {
405                throw new \ErrorException(
406                    'WPCrm requires php extension curl'
407                );
408            }
409            self::$api = new CrmApiV2();
410        }
411        return self::$api;
412    }
413
414    /**
415     * Make a call using apiV2
416     *
417     * @param object|string $obj       An object or a class name.
418     * @param string        $method    The method to call.
419     * @param array         $arguments Arguments to pass.
420     * @return mixed|false
421     * @throws LDC\LDCCRM\Exceptions\ApiErrorException If any of this exception is thrown.
422     * @throws LDC\LDCCRM\Exceptions\CrmException If a critical error occured.
423     */
424    private static function makeApiV2Call($obj, string $method, array $arguments = [])
425    {
426        try {
427            return \forward_static_call_array([$obj, $method], $arguments);
428        } catch (ApiErrorException $e) {
429            switch ($e->getApiError()) {
430                case ApiError::apiAuthFailed():
431                    Utils::log($e->getMessage(), LogLevel::EMERG());
432                    throw $e;
433                default:
434                    Utils::log($e->getMessage(), LogLevel::DEBUG());
435                    throw $e;
436            }
437        } catch (CrmException $e) {
438            Utils::log($e->getMessage(), LogLevel::EMERG());
439            throw $e;
440        }
441    }
442
443    /**
444     * Boot the php session if needed
445     *
446     * @return void
447     */
448    private static function bootSessionIfNeeded(): void
449    {
450        self::$sessionKey = ($_SERVER['HTTP_USER_AGENT'] ?? '') . ($_SERVER['REMOTE_ADDR'] ?? '');
451        /** @var string self::$sessionKey */
452        self::$sessionKey = \strlen(self::$sessionKey) ? self::$sessionKey : (date('d') ? date('d') : '');
453        if (!isset($_SESSION['wpcrm'])) {
454            $_SESSION['wpcrm'] = [];
455        }
456        if (!session_id() && !self::$sessionDestroyed) {
457            session_start();
458        }
459        if (
460            // If session identity is violated.
461            count($_SESSION['wpcrm']) > 1 or
462            // If session timed out.
463            (isset($_SESSION['wpcrm'][self::$sessionKey]) and
464                isset($_SESSION['lastActivity']) and
465            // * 15 minutes session.
466            ($_SESSION['lastActivity'] + (15 * 60)) < time())
467        ) {
468            // Destroy session.
469            self::$sessionDestroyed = true;
470            $_SESSION['wpcrm']      = [];
471        } else {
472            $_SESSION['lastActivity'] = time();
473        }
474        if (!self::$user and isset($_SESSION['wpcrm'][self::$sessionKey])) {
475            self::$user = WPCrmUser::parseFromJson(
476                $_SESSION['wpcrm'][self::$sessionKey]
477            );
478        }
479    }
480
481    /**
482     * Get a customer from table
483     *
484     * @param string $hashcode The customer hashcode.
485     *
486     * @return array|null Database query result in format specified by $output or null on failure.
487     */
488    public static function getUserFromTable(string $hashcode): ?array
489    {
490        return self::getWpDb()->get_row(
491            self::getWpDb()->prepare(
492                'SELECT * FROM `ldc_wp_customers` WHERE `hashcode` = %s',
493                [
494                    'hashcode' => $hashcode
495                ]
496            ),
497            'ARRAY_A'
498        );
499    }
500
501    /**
502     * Get a customer from table by an email
503     *
504     * @param string $email The customer email.
505     *
506     * @return array|null Database query result in format specified by $output or null on failure.
507     */
508    public static function getUserFromTableByEmail(string $email): ?array
509    {
510        return self::getWpDb()->get_row(
511            self::getWpDb()->prepare(
512                'SELECT * FROM `ldc_wp_customers` WHERE `email` = %s',
513                [
514                    'email' => $email
515                ]
516            ),
517            'ARRAY_A'
518        );
519    }
520
521    /**
522     * Get a customer from table by an email
523     *
524     * @param string $uuid The customer transaction uuid.
525     *
526     * @return array|null Database query result in format specified by $output or null on failure.
527     */
528    public static function getUserFromTableByUuid(string $uuid): ?array
529    {
530        return self::getWpDb()->get_row(
531            self::getWpDb()->prepare(
532                'SELECT * FROM `ldc_wp_customers` WHERE `uuid` = %s',
533                [
534                    'uuid' => $uuid
535                ]
536            ),
537            'ARRAY_A'
538        );
539    }
540
541    /**
542     * Save or update the user to the table
543     *
544     * @return integer The number of row affected on the table.
545     */
546    private static function saveOrUpdateUserToTable(): int
547    {
548        if (is_string(self::$user->hashcode) and self::getUserFromTable(self::$user->hashcode)) {
549            return self::query(
550                'UPDATE `ldc_wp_customers` SET `email` = %s, `uuid` = %s WHERE `hashcode` = %s',
551                [
552                    'email' => self::$user->email,
553                    'uuid' => self::$user->uuidToken,
554                    'hashcode' => self::$user->hashcode
555                ]
556            );
557        }
558        return self::query(
559            'INSERT INTO `ldc_wp_customers`(hashcode, email, uuid) VALUES (%s, %s, %s)',
560            [
561                'hashcode' => self::$user->hashcode,
562                'email' => self::$user->email,
563                'uuid' => self::$user->uuidToken
564            ]
565        );
566    }
567
568    /**
569     * Save or update the user to the table
570     *
571     * @return integer The number of row removed from the table.
572     */
573    private static function deleteUserFromTable(): int
574    {
575        return self::query(
576            'DELETE FROM `ldc_wp_customers` WHERE `hashcode` = %s;',
577            [
578                'hashcode' => self::$user->hashcode
579            ]
580        );
581    }
582
583    /**
584     * Query in DB
585     *
586     * @param string $query  La request avec les variables en points d'interrogation.
587     * @param array  $params Les paramètres des la requète.
588     *
589     * @return integer|boolean Boolean true for CREATE, ALTER, TRUNCATE and DROP queries.
590     * Number of rows affected/selected for all other queries. Boolean false on error.
591     */
592    private static function query(string $query, array $params = [])
593    {
594        return self::getWpDb()->query(
595            self::getWpDb()->prepare($query, $params)
596        );
597    }
598
599    /**
600     * Get WP 'pdo instance'
601     *
602     * @return object The $wpdb variable
603     */
604    private static function getWpDb(): object
605    {
606        // phpcs:ignore Squiz.PHP.GlobalKeyword.NotAllowed
607        global $wpdb;
608        return $wpdb;
609    }
610}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/WordPress/WPCrmAnswer.php.html b/tests/coverage/clients/V2/src/WordPress/WPCrmAnswer.php.html new file mode 100644 index 0000000..9c31eb1 --- /dev/null +++ b/tests/coverage/clients/V2/src/WordPress/WPCrmAnswer.php.html @@ -0,0 +1,203 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/WordPress/WPCrmAnswer.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
WPCrmAnswer
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 setQuestionAnswers
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3/**
4 * LDCCRM For Wordpress usage
5 *
6 * @category API
7 * @package  None
8 * @author   Jérémy Munsch <jeremy.munsch@ldc.fr>
9 * @license  MIT https://opensource.org/licenses/MIT
10 * @link     /
11 */
12
13namespace LDC\LDCCRM\WordPress;
14
15use LDC\LDCCRM\Models\Answer;
16
17/**
18 * Class de gestion de la segmentation question avec Wordpress (ce truc immonde)
19 *
20 * @category API
21 * @package LDCCrmClient
22 * @subpackage Wordpress
23 * @author   Jérémy Munsch <jeremy.munsch@ldc.fr>
24 * @license  MIT https://opensource.org/licenses/MIT
25 * @link     /
26 */
27abstract class WPCrmAnswer
28{
29    /**
30     * Set answers to a question for a specific user
31     *
32     * @param \LDC\LDCCRM\WordPress\WPCrmUser $user
33     * @param integer                         $questionId The question id .
34     * @param array                           $answersId  An array with answers id .
35     * @return boolean true if succeeded
36     *
37     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
38     * @throws \LDC\LDCCRM\Exceptions\ValidationException If $user hashcode is not a valid UUID.
39     * @throws \LDC\LDCCRM\Exceptions\ValidationException If any of the provided ids are invalid.
40     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error \LDC\LDCCRM\Enums\ApiError occurs.
41     * Possibles are : - validationFailed, If given ids are wrong(CRM side).
42     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
43     */
44    public function setQuestionAnswers(WPCrmUser $user, int $questionId, array $answersId): bool
45    {
46        return Answer::setQuestionAnswers($user->hashcode, $questionId, $answersId);
47    }
48}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/WordPress/WPCrmQuestion.php.html b/tests/coverage/clients/V2/src/WordPress/WPCrmQuestion.php.html new file mode 100644 index 0000000..f2e64e3 --- /dev/null +++ b/tests/coverage/clients/V2/src/WordPress/WPCrmQuestion.php.html @@ -0,0 +1,286 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/WordPress/WPCrmQuestion.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 9
WPCrmQuestion
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
30
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 9
 getAll
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 allWithCustomerAnswers
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
 unansweredBy
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3/**
4 * LDCCRM For Wordpress usage
5 *
6 * @category API
7 * @package  None
8 * @author   Jérémy Munsch <jeremy.munsch@ldc.fr>
9 * @license  MIT https://opensource.org/licenses/MIT
10 * @link     /
11 */
12
13namespace LDC\LDCCRM\WordPress;
14
15use LDC\LDCCRM\Models\Question;
16use LDC\LDCCRM\Utils;
17
18/**
19 * Class de gestion de la segmentation question avec Wordpress (ce truc immonde)
20 *
21 * @category API
22 * @package LDCCrmClient
23 * @subpackage Wordpress
24 * @author   Jérémy Munsch <jeremy.munsch@ldc.fr>
25 * @license  MIT https://opensource.org/licenses/MIT
26 * @link     /
27 */
28abstract class WPCrmQuestion
29{
30    /**
31     * Get all questions only
32     *
33     * @return array Of \LDC\LDCCRM\Models\Question
34     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
35     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
36     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
37     */
38    public static function getAll(): array
39    {
40        return Question::all();
41    }
42
43    /**
44     * Get all the questions with user answers
45     *
46     * @param \LDC\LDCCRM\WordPress\WPCrmUser $user
47     * @return array Of \LDC\LDCCRM\Models\Question and their \LDC\LDCCRM\Models\Answer.
48     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
49     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a hashcode is not a valid UUID.
50     * @throws \LDC\LDCCRM\Exceptions\ValidationException If any of the provided ids are invalid.
51     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
52     * Possibles are : - modelNotFound, If the customer could not be found(CRM side).
53     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
54     */
55    public static function allWithCustomerAnswers(WPCrmUser $user): array
56    {
57        // * Force loading of user info from crm
58        if (!Utils::getRawProperty($user, 'info')) {
59            WPCrm::getUserFromApi($user->hashcode);
60        }
61        // * Get the raw customer
62        /** @var \LDC\LDCCRM\Models\Customer */
63        $customer = Utils::getRawProperty($user, 'info');
64        return Question::allWithCustomerAnswers($customer);
65    }
66
67    /**
68     * Get unanswered question for a specific customer
69     *
70     * @param \LDC\LDCCRM\WordPress\WPCrmUser $user
71     * @return \LDC\LDCCRM\Models\Question[]
72     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
73     * @throws \LDC\LDCCRM\Exceptions\ValidationException If $user hashcode is not a valid UUID.
74     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
75     * Possibles are : - modelNotFound, If the customer could not be found(CRM side).
76     * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
77     */
78    public static function unansweredBy(WPCrmUser $user): array
79    {
80        // * Force loading of user info from crm
81        if (!Utils::getRawProperty($user, 'info')) {
82            WPCrm::getUserFromApi($user->hashcode);
83        }
84        // * Get the raw customer
85        /** @var \LDC\LDCCRM\Models\Customer */
86        $customer = Utils::getRawProperty($user, 'info');
87        return Question::unansweredBy($customer);
88    }
89}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/WordPress/WPCrmUser.php.html b/tests/coverage/clients/V2/src/WordPress/WPCrmUser.php.html new file mode 100644 index 0000000..8611915 --- /dev/null +++ b/tests/coverage/clients/V2/src/WordPress/WPCrmUser.php.html @@ -0,0 +1,615 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/WordPress/WPCrmUser.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 10
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 38
WPCrmUser
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 10
380
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 38
 __construct
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
 __get
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
20
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 7
 __set
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
30
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 11
 setInfo
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
 parseFromJson
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 7
 jsonSerialize
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
 save
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 delete
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 toggleOptin
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 activate
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3/**
4 * LDCCRM For Wordpress usage
5 *
6 * @category API
7 * @package  None
8 * @author   Jérémy Munsch <jeremy.munsch@ldc.fr>
9 * @license  MIT https://opensource.org/licenses/MIT
10 * @link     /
11 */
12
13namespace LDC\LDCCRM\WordPress;
14
15use JsonSerializable;
16use LDC\LDCCRM\Exceptions\WpCrmInvalidJsonUserInfo;
17use LDC\LDCCRM\Models\Customer;
18
19/**
20 * Class de gestion des users en session avec Wordpress (ce truc immonde)
21 *
22 * @property string $uuidToken — A local transaction token for this user
23 *
24 *
25 * @property string $hashcode — The crm hashcode this is a readonly field
26 *
27 * @property string $email — Must be a rfc valid email on a valid domain name
28 *
29 * @property string $password
30 * Required only on create,
31 * must have upper and lowers chars,
32 * at least 1 number,
33 * at leat 1 upper char,
34 * at least 1 lower char and
35 * at leat 1 special char
36 *
37 * @property string $firstname — The account first name
38 *
39 * @property string $lastname — The account last name
40 *
41 * @property string $birth_date — The account birth date 'yyyy-mm-dd'
42 *
43 * @property string $phone — The account phone
44 *
45 * @property string $address
46 * The account address (street number, name, build, flat number)
47 *
48 * @property string $address2 — The account phone (address complement)
49 *
50 * @property string $city — The account city name
51 *
52 * @property string $postcode — The account city postcode
53 *
54 * @property \LDC\LDCCRM\Enums\Country $country — The account country residence
55 *
56 * @property bool $opt_in — true if the account is opt in
57 *
58 * @property bool $status — true if the account is activated
59 *
60 * @property string $last_connection the last time user was connected 'yyyy-mm-dd HH:ii:ss'
61 *
62 * @category API
63 * @package LDCCrmClient
64 * @subpackage Wordpress
65 * @author   Jérémy Munsch <jeremy.munsch@ldc.fr>
66 * @license  MIT https://opensource.org/licenses/MIT
67 * @link     /
68 */
69class WPCrmUser implements JsonSerializable
70{
71    /**
72     * The CRM hashcode (UUID)
73     *
74     * @var string
75     */
76    protected $phashcode = null;
77
78    /**
79     * The CRM customer email
80     *
81     * @var string
82     */
83    protected $pemail = null;
84
85    /**
86     * The CRM customer info (the api response)
87     *
88     * @var \LDC\LDCCRM\Models\Customer
89     */
90    protected $info = null;
91
92    /**
93     * The CRM customer temp uuidToken to validate transactions
94     *
95     * @var string UUID
96     */
97    protected $uuidToken = null;
98
99    /**
100     * WPCrmUser construct
101     *
102     * @param string $hashcode The customer hashcode (UUID).
103     * @param string $email    The customer email.
104     */
105    public function __construct(string $hashcode = null, string $email = null)
106    {
107        $this->phashcode = $hashcode;
108        $this->pemail    = $email;
109    }
110
111    /**
112     * Generic accessor
113     *
114     * @param string $prop The prop to get.
115     *
116     * @return boolean|string|\LDC\LDCCRM\Enums\Country|null
117     */
118    public function __get(string $prop)
119    {
120        if ($prop === 'uuidToken') {
121            return $this->uuidToken;
122        }
123        if (is_null($this->info)) {
124            if (!is_null($this->pemail)) {
125                WPCrm::getUserFromApi($this->pemail);
126            } else {
127                $this->info = new Customer();
128            }
129        }
130        return $this->info->{$prop};
131    }
132
133    /**
134     * Generic mutator
135     *
136     * @param string $prop  The property to assign.
137     * @param mixed  $value The value to set.
138     *
139     * @return void
140     */
141    public function __set(string $prop, $value): void
142    {
143        if ($prop === 'uuidToken') {
144            $this->uuidToken = $value;
145            return;
146        }
147        if (is_null($this->info)) {
148            if (!is_null($this->pemail)) {
149                WPCrm::getUserFromApi($this->pemail);
150            } else {
151                $this->info = new Customer();
152            }
153        }
154        $this->info->{$prop} = $value;
155        if ($prop === 'email') {
156            $this->pemail = $value;
157        }
158    }
159
160    /**
161     * Set the customer info
162     * with api response
163     *
164     * @param \LDC\LDCCRM\Models\Customer $customer The Api object reponse (user).
165     *
166     * @return void
167     */
168    public function setInfo(Customer $customer)
169    {
170        $this->info      = $customer;
171        $this->pemail    = $customer->email;
172        $this->phashcode = $customer->hashcode;
173    }
174
175    /**
176     * Generate a user from session data
177     *
178     * @param string $string The json data stored in session.
179     *
180     * @return \LDC\LDCCRM\WordPress\WPCrmUser
181     * @throws \LDC\LDCCRM\Exceptions\WpCrmInvalidJsonUserInfo If the json string does no
182     *  contain the needed information.
183     */
184    public static function parseFromJson(string $string): WPCrmUser
185    {
186        $a = json_decode($string, true);
187        if (!isset($a['hashcode']) or !isset($a['email'])) {
188            throw new WpCrmInvalidJsonUserInfo(\sprintf(
189                '%s::%s is missing some information to parse from, uuid was %s',
190                __CLASS__,
191                __METHOD__,
192                $a['hashcode']
193            ));
194        }
195        return new self(
196            $a['hashcode'],
197            $a['email']
198        );
199    }
200
201    /**
202     * Serialize this object to an array
203     *
204     * @return array
205     */
206    public function jsonSerialize()
207    {
208        return [
209            'hashcode' => $this->phashcode,
210            'email' => $this->pemail
211        ];
212    }
213
214    /**
215     * Save current customer to
216     * the local database and on crm
217     *
218     * @return boolean true if the operation succeeded false otherwise
219     * @throws LDC\LDCCRM\Exceptions\CrmException If a critical error occured.
220     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
221     * Possibles are : - validationFailed, If provided data is wrong(CRM side).
222     *                 - modelNotFound, If this customer hashcode could not be found.
223     */
224    public function save(): bool
225    {
226        return WPCrm::saveUser();
227    }
228
229    /**
230     * Delete current customer to
231     * the local database and on crm
232     *
233     * @return boolean true if the operation succeeded false otherwise
234     * @throws LDC\LDCCRM\Exceptions\CrmException If a critical error occured.
235     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
236     * Possibles are : - modelNotFound, If this customer hashcode could not be found.
237     */
238    public function delete(): bool
239    {
240        return WPCrm::deleteUser();
241    }
242
243    /**
244     * Toggle the current user optin value
245     * on the current user and the crm
246     *
247     * @return boolean true if the operation succeeded
248     * @throws LDC\LDCCRM\Exceptions\CrmException If a critical error occured.
249     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
250     * Possibles are : - modelNotFound, If this customer hashcode could not be found.
251     */
252    public function toggleOptin(): bool
253    {
254        return WPCrm::toggleUserOptin();
255    }
256
257    /**
258     * Activate the account in the crm
259     *
260     * @return boolean true if succeeded
261     * @throws \LDC\LDCCRM\Exceptions\ValidationException If a customer's hashcode could not be validated.
262     * @throws \LDC\LDCCRM\Exceptions\CrmException If an error occurs on server side or on getting response.
263     * @throws \LDC\LDCCRM\Exceptions\ApiErrorException If any error or \LDC\LDCCRM\Enums\ApiError occurs.
264     * Possibles are : - modelNotFound, If this customer hashcode could not be found.
265     *                 - customerAlreadyActive, It the customer is already activated.
266     */
267    public function activate(): bool
268    {
269        return WPCrm::activateUser();
270    }
271}
+ + + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/WordPress/dashboard.html b/tests/coverage/clients/V2/src/WordPress/dashboard.html new file mode 100644 index 0000000..992c48d --- /dev/null +++ b/tests/coverage/clients/V2/src/WordPress/dashboard.html @@ -0,0 +1,350 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/clients/V2/src/WordPress + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+ +
+
+

Project Risks

+ +
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+ + +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/WordPress/index.html b/tests/coverage/clients/V2/src/WordPress/index.html new file mode 100644 index 0000000..3c1041f --- /dev/null +++ b/tests/coverage/clients/V2/src/WordPress/index.html @@ -0,0 +1,206 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src/WordPress + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 203
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 39
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
WPCrm.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 155
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 25
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
WPCrmAnswer.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
WPCrmQuestion.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 9
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
WPCrmUser.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 38
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/tests/coverage/clients/V2/src/dashboard.html b/tests/coverage/clients/V2/src/dashboard.html new file mode 100644 index 0000000..50c385f --- /dev/null +++ b/tests/coverage/clients/V2/src/dashboard.html @@ -0,0 +1,435 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/clients/V2/src + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+ +
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+ + +
+ +
+ + + + + + diff --git a/tests/coverage/clients/V2/src/index.html b/tests/coverage/clients/V2/src/index.html new file mode 100644 index 0000000..b63ead6 --- /dev/null +++ b/tests/coverage/clients/V2/src/index.html @@ -0,0 +1,289 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients/V2/src + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 67.67% covered (warning) +
+
+
67.67%
720 / 1064
+
+ 47.77% covered (danger) +
+
+
47.77%
75 / 157
+
+ 30.77% covered (danger) +
+
+
30.77%
8 / 26
Enums
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
+
+ 66.67% covered (warning) +
+
+
66.67%
4 / 6
Exceptions
+
+ 66.67% covered (warning) +
+
+
66.67%
10 / 15
+
+ 50.00% covered (danger) +
+
+
50.00%
3 / 6
+
+ 33.33% covered (danger) +
+
+
33.33%
1 / 3
Models
+
+ 87.32% covered (warning) +
+
+
87.32%
372 / 426
+
+ 64.81% covered (warning) +
+
+
64.81%
35 / 54
+
+ 25.00% covered (danger) +
+
+
25.00%
2 / 8
Transport
+
+ 85.06% covered (warning) +
+
+
85.06%
131 / 154
+
+ 50.00% covered (danger) +
+
+
50.00%
9 / 18
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
WordPress
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 203
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 39
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
CrmApiV2.php
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
Utils.php
+
+ 77.02% covered (warning) +
+
+
77.02%
191 / 248
+
+ 61.54% covered (warning) +
+
+
61.54%
16 / 26
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/tests/coverage/clients/dashboard.html b/tests/coverage/clients/dashboard.html new file mode 100644 index 0000000..202cbc6 --- /dev/null +++ b/tests/coverage/clients/dashboard.html @@ -0,0 +1,433 @@ + + + + + Dashboard for /home/kwaadpepper/projets/crm/clients + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+ +
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+ + +
+ +
+ + + + + + diff --git a/tests/coverage/clients/index.html b/tests/coverage/clients/index.html new file mode 100644 index 0000000..ecfba81 --- /dev/null +++ b/tests/coverage/clients/index.html @@ -0,0 +1,119 @@ + + + + + Code Coverage for /home/kwaadpepper/projets/crm/clients + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 67.67% covered (warning) +
+
+
67.67%
720 / 1064
+
+ 47.77% covered (danger) +
+
+
47.77%
75 / 157
+
+ 30.77% covered (danger) +
+
+
30.77%
8 / 26
V2
+
+ 67.67% covered (warning) +
+
+
67.67%
720 / 1064
+
+ 47.77% covered (danger) +
+
+
47.77%
75 / 157
+
+ 30.77% covered (danger) +
+
+
30.77%
8 / 26
+
+ +
+ + diff --git a/tests/coverage/dashboard.html b/tests/coverage/dashboard.html new file mode 100644 index 0000000..7df9825 --- /dev/null +++ b/tests/coverage/dashboard.html @@ -0,0 +1,328 @@ + + + + + Dashboard for /home/jeremy/projets/crm/app + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+ +
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+ +
+

Project Risks

+ +
+
+ +
+ + + + + + diff --git a/tests/coverage/index.html b/tests/coverage/index.html new file mode 100644 index 0000000..091507a --- /dev/null +++ b/tests/coverage/index.html @@ -0,0 +1,310 @@ + + + + + Code Coverage for /home/jeremy/projets/crm/app + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 93.58% covered (success) +
+
+
93.58%
816 / 872
+
+ 82.22% covered (warning) +
+
+
82.22%
111 / 135
+
+ 78.43% covered (warning) +
+
+
78.43%
40 / 51
Console
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Enums
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
5 / 5
Exceptions
+
+ 88.00% covered (warning) +
+
+
88.00%
44 / 50
+
+ 62.50% covered (warning) +
+
+
62.50%
5 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
Http
+
+ 99.11% covered (success) +
+
+
99.11%
443 / 447
+
+ 96.23% covered (success) +
+
+
96.23%
51 / 53
+
+ 93.10% covered (success) +
+
+
93.10%
27 / 29
Jobs
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Lib
+
+ 80.00% covered (warning) +
+
+
80.00%
48 / 60
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
Mail
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Models
+
+ 86.77% covered (warning) +
+
+
86.77%
223 / 257
+
+ 69.64% covered (warning) +
+
+
69.64%
39 / 56
+
+ 50.00% covered (danger) +
+
+
50.00%
6 / 12
Policies
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
Providers
+
+ 100.00% covered (success) +
+
+
100.00%
51 / 51
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ +
+ +