From 78a166af00a0d4164cebf3d0e045d5523c3a9363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 28 Jun 2023 15:16:37 +0200 Subject: [PATCH] Add Api Remove single optin using hashcode --- .../Controllers/Api/V2/OptinController.php | 56 ++++++++++++++++--- .../SingleOptinRemoveWithHashcodeRequest.php | 18 ++++++ app/Models/CustomerInfo.php | 14 +++++ composer.json | 2 +- routes/api.php | 1 + tests/CRM/ApiV2/ApiBase.php | 2 +- tests/CRM/ApiV2/SingleOptinTest.php | 56 +++++++++++++++++++ tests/Client/CrmClientV2SingleOptinTest.php | 21 +++++++ 8 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 app/Http/Requests/Api/V2/SingleOptinRemoveWithHashcodeRequest.php diff --git a/app/Http/Controllers/Api/V2/OptinController.php b/app/Http/Controllers/Api/V2/OptinController.php index d5c8415..fd3b798 100644 --- a/app/Http/Controllers/Api/V2/OptinController.php +++ b/app/Http/Controllers/Api/V2/OptinController.php @@ -6,7 +6,9 @@ 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\BaseApiRequest; use App\Http\Requests\Api\V2\SingleOptinRequest; +use App\Http\Requests\Api\V2\SingleOptinRemoveWithHashcodeRequest; use App\Models\CustomerInfo; use App\Models\Service; use Illuminate\Http\JsonResponse; @@ -36,15 +38,7 @@ class OptinController extends Controller $q->where('id', $service->brand->id); })->firstOrNew(); - if ($request->opt_in) { - $singleOptin->fill($request->validated()); - $singleOptin->brand()->associate($service->brand); - $success = $singleOptin->save(); - } else { - $success = $singleOptin->delete() !== false; - } - - if (!$success) { + if (!$this->changeOptin($service, $request, \boolval($request->opt_in), $singleOptin)) { // @codeCoverageIgnoreStart throw new CrmException('Api failed to update single optin.'); // @codeCoverageIgnoreEnd @@ -52,4 +46,48 @@ class OptinController extends Controller return response()->json(); } + + /** + * Creates a new Single optin or destroys it if exists using hashcode + * + * @param \App\Http\Requests\Api\V2\SingleOptinRemoveWithHashcodeRequest $request + * @param \App\Models\Service $service + * @return \Illuminate\Http\JsonResponse + * @throws \App\Exceptions\Api\V2\CrmException If the single optin could not be removed. + */ + public function optoutWithHashcode(SingleOptinRemoveWithHashcodeRequest $request, Service $service) + { + /** @var \App\Models\SingleOptin */ + $singleOptin = SingleOptin::query()->where('hashcode', $request->hashcode) + ->whereHas('brand', function ($q) use ($service) { + $q->whereId($service->brand->id); + })->firstOrFail(); + + if ($singleOptin and !$this->changeOptin($service, $request, \boolval($request->opt_in), $singleOptin)) { + // @codeCoverageIgnoreStart + throw new CrmException('Api failed to update single optin.'); + // @codeCoverageIgnoreEnd + } + + return response()->json(); + } + + /** + * Changes the optin state + * + * @param \App\Models\Service $service + * @param \App\Http\Requests\Api\V2\BaseApiRequest $request + * @param boolean $optin + * @param \App\Models\SingleOptin $singleOptin + * @return boolean Returns false in case of failure. + */ + private function changeOptin(Service $service, BaseApiRequest $request, bool $optin, SingleOptin $singleOptin): bool + { + if ($optin) { + $singleOptin->fill($request->validated()); + $singleOptin->brand()->associate($service->brand); + return $singleOptin->save(); + } + return $singleOptin->delete() !== false; + } } diff --git a/app/Http/Requests/Api/V2/SingleOptinRemoveWithHashcodeRequest.php b/app/Http/Requests/Api/V2/SingleOptinRemoveWithHashcodeRequest.php new file mode 100644 index 0000000..009bd46 --- /dev/null +++ b/app/Http/Requests/Api/V2/SingleOptinRemoveWithHashcodeRequest.php @@ -0,0 +1,18 @@ + 'required|string|size:36' + ]; + } +} diff --git a/app/Models/CustomerInfo.php b/app/Models/CustomerInfo.php index 241050d..1b381c4 100644 --- a/app/Models/CustomerInfo.php +++ b/app/Models/CustomerInfo.php @@ -118,4 +118,18 @@ class CustomerInfo extends Model $q->where('email', $email); }); } + + /** + * Get customer info from hashcode + * + * @param \App\Models\Brand $brand + * @param string $hashcode + * @return \Illuminate\Database\Eloquent\Builder + */ + public static function getFromHashcode(Brand $brand, string $hashcode) + { + return CustomerInfo::query()->whereHas('brand', function (Builder $q) use ($brand) { + $q->where('id', $brand->id); + })->where('hashcode', $hashcode); + } } diff --git a/composer.json b/composer.json index 0ee68d7..c70db87 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "ldc/ldccrmv2", "type": "project", - "version": "3.1.0", + "version": "3.0.6", "description": "LDC CRM V2 - revamped", "license": "MIT", "require": { diff --git a/routes/api.php b/routes/api.php index 8527a5a..511d397 100644 --- a/routes/api.php +++ b/routes/api.php @@ -51,6 +51,7 @@ Route::prefix('v2')->name('apiV2.')->group(function () { // * OPTINS Route::post('/optin', [V2OptinController::class, 'optin'])->name('optin'); + Route::post('/optout', [V2OptinController::class, 'optoutWithHashcode'])->name('optout-hashcode'); // * CUSTOMERS Route::apiResource('customers', V2CustomerController::class, [ diff --git a/tests/CRM/ApiV2/ApiBase.php b/tests/CRM/ApiV2/ApiBase.php index b2d8ee8..6f1329e 100644 --- a/tests/CRM/ApiV2/ApiBase.php +++ b/tests/CRM/ApiV2/ApiBase.php @@ -86,7 +86,7 @@ class ApiBase extends BaseTestCase */ protected function getCustomer(bool $status = true, bool $random = true): array { - $login = CustomerLogin::whereStatus($status); + $login = CustomerLogin::query()->where('status', $status); if ($random) { $login = $login->inRandomOrder(); } diff --git a/tests/CRM/ApiV2/SingleOptinTest.php b/tests/CRM/ApiV2/SingleOptinTest.php index 36d7c55..2799e5c 100644 --- a/tests/CRM/ApiV2/SingleOptinTest.php +++ b/tests/CRM/ApiV2/SingleOptinTest.php @@ -87,6 +87,62 @@ class SingleOptinTest extends ApiBase $this->assertEquals(ApiError::singleOptinCustomerExists()->label, $json->json('data.message')); } + /** + * Test set single optin off using hashcode + * route: apiV2.optout-hashcode + * + * @return void + */ + public function testSetSingleOptoutWithHashcode() + { + $service = Service::firstOrFail(); + + // * Make sure a single optin exists. + $singleOptin = SingleOptin::query()->where('email', 'singleoptout@hotmail.fr')->firstOrNew(); + $singleOptin->brand()->associate($service->brand); + $singleOptin->email = 'singleoptout@hotmail.fr'; + $singleOptin->saveOrFail(); + $singleOptin->refresh(); + + $response = $this->callApiWithToken($service, 'post', route('apiV2.optout-hashcode'), [ + 'hashcode' => $singleOptin->hashcode + ]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::OK()); + $this->assertFalse( + SingleOptin::query()->where('email', 'singleoptout@hotmail.fr')->exists(), + 'Single optin should have been removed for email "singleoptout@hotmail.fr"' + ); + } + + /** + * Test set single optin off using hashcode fails if does not exists + * route: apiV2.optout-hashcode + * + * @return void + */ + public function testSetSingleOptoutWithHashcodeFailsIfDoesNotExists() + { + $service = Service::firstOrFail(); + + // * Make sure a single optin exists. + $singleOptin = SingleOptin::query()->where('email', 'singleoptout@hotmail.fr')->firstOrNew(); + $singleOptin->brand()->associate($service->brand); + $singleOptin->email = 'singleoptout@hotmail.fr'; + $singleOptin->saveOrFail(); + $singleOptin->refresh(); + + // * Make sure a single optin does exists. + $singleOptin->delete(); + + $response = $this->callApiWithToken($service, 'post', route('apiV2.optout-hashcode'), [ + 'hashcode' => $singleOptin->hashcode + ]); + $json = $response->decodeResponseJson(); + $this->assertApiResponseStatusIs($json, ApiStatus::KO()); + $this->assertEquals(ApiError::modelNotFound()->value, $json->json('data.code')); + } + /** * Store a customer in DB * diff --git a/tests/Client/CrmClientV2SingleOptinTest.php b/tests/Client/CrmClientV2SingleOptinTest.php index 0732781..f51d675 100644 --- a/tests/Client/CrmClientV2SingleOptinTest.php +++ b/tests/Client/CrmClientV2SingleOptinTest.php @@ -61,4 +61,25 @@ class CrmClientV2SingleOptinTest extends CrmClientTester $this->assertTrue($customer->save()); $this->assertFalse(SingleOptin::setStatus($randomMail, true)); } + + /** + * Remove single optin with hashcode test + * + * Calling multiple times single optin true should not fail. + * + * @return void + */ + public function testRemoveSingleOptinWithHashcode() + { + // Set single optin and check. + $this->assertTrue(SingleOptin::setStatus('singleoptinrandom@gmail.com', true)); + $this->assertTrue(ModelsSingleOptin::query()->where('email', 'singleoptinrandom@gmail.com')->exists()); + + $hashcode = ModelsSingleOptin::query() + ->where('email', 'singleoptinrandom@gmail.com')->sole('hashcode')->hashcode; + + // Remove single optin test and check. + $this->assertTrue(SingleOptin::unsetStatusWithHashcode($hashcode)); + $this->assertTrue(!ModelsSingleOptin::query()->where('email', 'singleoptinrandom@gmail.com')->exists()); + } }