From 787ee5a4c57b5ad2974e6e9ef9874df97f07bf9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 11 Aug 2021 17:25:24 +0200 Subject: [PATCH] - tag 2.0.1 - Add Questions segementation - Fix Customer delete Tests --- app/Console/Commands/GenerateCrmClient.php | 4 +- .../Controllers/Api/V2/AnswerController.php | 22 +++++- .../Controllers/Api/V2/ArcadiaController.php | 2 + .../Controllers/Api/V2/CustomerController.php | 32 ++++++-- .../Controllers/Api/V2/QuestionController.php | 63 +++++++++++++++- .../Requests/Api/V2/StoreCustomerAnswers.php | 47 ++++++++++++ app/Http/Resources/Api/V2/AnswersResource.php | 26 +++++++ .../Resources/Api/V2/QuestionResource.php | 34 +++++++++ .../Resources/Api/V2/QuestionsResource.php | 49 ++++++++++++ app/Models/Answer.php | 8 +- app/Models/Brand.php | 10 +++ app/Models/Customer.php | 10 +++ app/Models/CustomerOptInHistory.php | 10 +++ app/Policies/CustomerPolicy.php | 17 +++++ app/Providers/AuthServiceProvider.php | 1 + clients/V2 | 2 +- composer.json | 2 +- database/factories/AnswerFactory.php | 8 +- database/factories/QuestionFactory.php | 2 +- ...10_104859_create_customer_answer_table.php | 2 +- database/seeders/BrandSeeder.php | 10 +++ routes/api.php | 15 +++- tests/Feature/ApiV2/ApiBase.php | 8 +- tests/Feature/ApiV2/ArcadiaTest.php | 2 +- tests/Feature/ApiV2/CustomerTest.php | 34 ++++----- tests/Feature/ApiV2/QuestionTest.php | 72 ++++++++++++++++++ tests/Feature/ApiV2/SingleOptinTest.php | 2 +- tests/Unit/CrmClientV2CustomerTest.php | 4 +- tests/Unit/CrmClientV2QuestionTest.php | 75 +++++++++++++++++++ tests/Unit/CrmClientV2RouterTest.php | 6 ++ 30 files changed, 533 insertions(+), 46 deletions(-) create mode 100644 app/Http/Requests/Api/V2/StoreCustomerAnswers.php create mode 100644 app/Http/Resources/Api/V2/AnswersResource.php create mode 100644 app/Http/Resources/Api/V2/QuestionResource.php create mode 100644 app/Http/Resources/Api/V2/QuestionsResource.php create mode 100644 app/Policies/CustomerPolicy.php create mode 100644 tests/Feature/ApiV2/QuestionTest.php create mode 100644 tests/Unit/CrmClientV2QuestionTest.php diff --git a/app/Console/Commands/GenerateCrmClient.php b/app/Console/Commands/GenerateCrmClient.php index 35e1618..a19ceed 100644 --- a/app/Console/Commands/GenerateCrmClient.php +++ b/app/Console/Commands/GenerateCrmClient.php @@ -7,6 +7,7 @@ use App\Enums\ApiStatus; use App\Enums\ArcadiaMailType; use App\Enums\ArcadiaOrderType; use App\Enums\Country; +use App\Enums\QuestionType; use App\Exceptions\Api\V2\ApiErrorException; use App\Exceptions\Api\V2\CrmException; use Illuminate\Console\Command; @@ -106,7 +107,8 @@ class GenerateCrmClient extends Command ApiStatus::class, ArcadiaMailType::class, ArcadiaOrderType::class, - Country::class + Country::class, + QuestionType::class ], 'Enums'); } diff --git a/app/Http/Controllers/Api/V2/AnswerController.php b/app/Http/Controllers/Api/V2/AnswerController.php index 64bca6d..86bda2f 100644 --- a/app/Http/Controllers/Api/V2/AnswerController.php +++ b/app/Http/Controllers/Api/V2/AnswerController.php @@ -3,8 +3,28 @@ namespace App\Http\Controllers\Api\V2; use App\Http\Controllers\Controller; +use App\Http\Requests\Api\V2\StoreCustomerAnswers; +use App\Models\Answer; +use App\Models\Customer; +use App\Models\Service; +use Illuminate\Http\JsonResponse; 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 + */ + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed + public function storeCustomerAnswers(StoreCustomerAnswers $request, Service $service, Customer $customer): JsonResponse + { + $this->authorize('customerBelongsToService', $customer); + $answersID = Answer::whereIn('id', $request->answers)->pluck('id')->toArray(); + $customer->answers()->sync($answersID); + return \response()->json(); + } } diff --git a/app/Http/Controllers/Api/V2/ArcadiaController.php b/app/Http/Controllers/Api/V2/ArcadiaController.php index d0be251..a30ba1f 100644 --- a/app/Http/Controllers/Api/V2/ArcadiaController.php +++ b/app/Http/Controllers/Api/V2/ArcadiaController.php @@ -32,11 +32,13 @@ class ArcadiaController extends Controller { public function outgoingMailDefault(SendArchadiaDefaultMailRequest $request, Customer $customer, Service $service) { + $this->authorize('customerBelongsToService', $customer); return $this->handleArcadiaMail($request, ArcadiaMailType::default(), $customer, $service); } public function outgoingMailGame(SendArchadiaGameMailRequest $request, Customer $customer, Service $service) { + $this->authorize('customerBelongsToService', $customer); return $this->handleArcadiaMail($request, ArcadiaMailType::game(), $customer, $service); } diff --git a/app/Http/Controllers/Api/V2/CustomerController.php b/app/Http/Controllers/Api/V2/CustomerController.php index f8db182..f855456 100644 --- a/app/Http/Controllers/Api/V2/CustomerController.php +++ b/app/Http/Controllers/Api/V2/CustomerController.php @@ -73,6 +73,7 @@ class CustomerController extends Controller */ public function show(Customer $customer, Service $service): JsonResource { + $this->authorize('customerBelongsToService', $customer); return new CustomerResource($service->brand, $customer); } @@ -86,6 +87,7 @@ class CustomerController extends Controller */ 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) { @@ -117,6 +119,7 @@ class CustomerController extends Controller */ public function destroy(Customer $customer, Service $service): JsonResponse { + $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 /** @var \App\Models\CustomerInfo */ @@ -124,13 +127,31 @@ class CustomerController extends Controller /** @var \App\Models\CustomerLogin */ $customerLogin = $customer->login($service->brand)->firstOrFail(); - if (!$customerInfo->delete() || !$customerLogin->delete()) { - throw new CrmException(sprintf('Could not delete customer %d for brand %d', $customer, $service->brand)); - } + if ( + !$customerInfo->delete() || + !$customerLogin->delete() || + $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 + )); + }; - if (!$customer->logins()->count() and !$customer->infos()->count()) { + $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 CrmException(sprintf('Could not delete customer %d', $customer)); + throw new CrmException(sprintf( + 'Could not delete customer %d', + $customer->id + )); } } @@ -176,6 +197,7 @@ class CustomerController extends Controller */ public function activateAccount(Customer $customer, Service $service) { + $this->authorize('customerBelongsToService', $customer); $login = $customer->login($service->brand)->firstOrFail(); if ($login->status === 0) { diff --git a/app/Http/Controllers/Api/V2/QuestionController.php b/app/Http/Controllers/Api/V2/QuestionController.php index 9b5d7b4..3d53a6e 100644 --- a/app/Http/Controllers/Api/V2/QuestionController.php +++ b/app/Http/Controllers/Api/V2/QuestionController.php @@ -3,8 +3,69 @@ namespace App\Http\Controllers\Api\V2; use App\Http\Controllers\Controller; +use App\Http\Resources\Api\V2\QuestionsResource; +use App\Models\Answer; +use App\Models\Customer; +use App\Models\Question; +use App\Models\Service; +use Illuminate\Http\Resources\Json\JsonResource; +use Illuminate\Http\Resources\Json\ResourceCollection; class QuestionController extends Controller { - // + /** + * Display all specified resources. + * + * @param \App\Models\Service $service + * @return \Illuminate\Http\Resources\Json\JsonResource + */ + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed + public function index(Service $service): ResourceCollection + { + return new QuestionsResource($service->brand->questions); + } + + /** + * Display all specified resources. + * + * @param \App\Models\Service $service + * @return \Illuminate\Http\Resources\Json\JsonResource + */ + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed + public function indexWithResponse(Service $service, Customer $customer): ResourceCollection + { + $this->authorize('customerBelongsToService', $customer); + $customerAnswers = Answer::whereHas('customers', function ($q) use ($customer) { + $q->whereId($customer->id); + })->get(); + $customerAnswers = Answer::whereHas('customers', function ($q) use ($customer) { + $q->whereId($customer->id); + })->whereHas('question', function ($q) use ($service) { + $q->whereHas('brand', function ($q) use ($service) { + $q->whereId($service->brand->id); + }); + })->get(); + return new QuestionsResource($service->brand->questions, $customerAnswers); + } + + /** + * Display all specified resource by customer. + * + * @param \App\Models\Service $service + * @param \App\Models\Customer $customer + * @return \Illuminate\Http\Resources\Json\JsonResource + */ + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed + public function unAnsweredBy(Service $service, Customer $customer): JsonResource + { + $this->authorize('customerBelongsToService', $customer); + $questions = Question::whereHas('brand', function ($q) use ($service) { + $q->where('id', $service->brand->id); + })->whereDoesntHave('answers', function ($q) use ($customer) { + $q->whereIn('id', Answer::whereHas('customers', function ($q) use ($customer) { + $q->where('id', $customer->id); + })->pluck('id')); + })->with('answers')->get(); + return new QuestionsResource($questions); + } } diff --git a/app/Http/Requests/Api/V2/StoreCustomerAnswers.php b/app/Http/Requests/Api/V2/StoreCustomerAnswers.php new file mode 100644 index 0000000..0a96d18 --- /dev/null +++ b/app/Http/Requests/Api/V2/StoreCustomerAnswers.php @@ -0,0 +1,47 @@ +make(\App\Models\Service::class); + $brandId = $service->brand->id; + $question = Question::findOrFail($this->question); + $questionTableName = (new Question())->getTable(); + $answerTableName = (new Answer())->getTable(); + return [ + 'question' => [ + 'required', + "exists:$questionTableName,id", + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed + function ($attribute, $value, $fail) use ($question, $brandId) { + if ($question->brand->id !== $brandId) { + $fail("$attribute does not belong to brand $brandId"); + } + } + ], + 'answers' => 'required|array', + 'answers.*' => [ + 'int', + 'distinct', + "exists:$answerTableName,id", + function ($attribute, $value, $fail) use ($question) { + if (Answer::findOrFail($value)->question->id !== $question->id) { + $fail("$attribute does not belong to question $question->id"); + } + } + ] + ]; + } +} diff --git a/app/Http/Resources/Api/V2/AnswersResource.php b/app/Http/Resources/Api/V2/AnswersResource.php new file mode 100644 index 0000000..07b32b9 --- /dev/null +++ b/app/Http/Resources/Api/V2/AnswersResource.php @@ -0,0 +1,26 @@ +collection->map(function (Answer $answer) { + return [ + 'id' => $answer->id, + 'answer' => $answer->answer + ]; + }); + } +} diff --git a/app/Http/Resources/Api/V2/QuestionResource.php b/app/Http/Resources/Api/V2/QuestionResource.php new file mode 100644 index 0000000..497a633 --- /dev/null +++ b/app/Http/Resources/Api/V2/QuestionResource.php @@ -0,0 +1,34 @@ +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/QuestionsResource.php b/app/Http/Resources/Api/V2/QuestionsResource.php new file mode 100644 index 0000000..a3c64e7 --- /dev/null +++ b/app/Http/Resources/Api/V2/QuestionsResource.php @@ -0,0 +1,49 @@ +userAnwsers = $userAnwsers; + parent::__construct($resource); + } + + /** + * Transform the resource into an array. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass + public function toArray($request) + { + return $this->collection->map(function (Question $question) { + $question = [ + 'id' => $question->id, + 'type' => $question->type->value, + 'question' => $question->question, + 'answers' => new AnswersResource($question->answers) + ]; + if ($this->userAnwsers) { + $question['customerAnswers'] = new AnswersResource($this->userAnwsers); + } + return $question; + }); + } +} diff --git a/app/Models/Answer.php b/app/Models/Answer.php index 58df0f8..f85e234 100644 --- a/app/Models/Answer.php +++ b/app/Models/Answer.php @@ -24,12 +24,12 @@ class Answer extends Model } /** - * Answer hasOneThrough relation + * Answer belongsToMany relation * - * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ - public function customer() + public function customers() { - return $this->hasOneThrough(Customer::class, 'customer_answer'); + return $this->belongsToMany(Customer::class, 'customer_answer'); } } diff --git a/app/Models/Brand.php b/app/Models/Brand.php index 8900982..a628480 100644 --- a/app/Models/Brand.php +++ b/app/Models/Brand.php @@ -23,6 +23,16 @@ class Brand extends Model return $this->hasMany(Service::class); } + /** + * Question hasMany relation + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function questions() + { + return $this->hasMany(Question::class); + } + /** * CustomerInfo hasMany relation * diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 2a72b53..935da9c 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -84,6 +84,16 @@ class Customer extends Model return $this->belongsToMany(Brand::class, CustomerInfo::class); } + /** + * Answers belongsToMany relation + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function answers() + { + return $this->belongsToMany(Answer::class, 'customer_answer'); + } + public function setOptIn(Brand $brand, bool $optIn) { // Ajout dans la table d'historique des opt-in de la nouvelle valeur diff --git a/app/Models/CustomerOptInHistory.php b/app/Models/CustomerOptInHistory.php index 5228320..6d2ca9a 100644 --- a/app/Models/CustomerOptInHistory.php +++ b/app/Models/CustomerOptInHistory.php @@ -20,4 +20,14 @@ class CustomerOptInHistory extends Model { return $this->belongsTo(Customer::class); } + + /** + * Brand BelongsTo relation + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function brand() + { + return $this->belongsTo(Brand::class); + } } diff --git a/app/Policies/CustomerPolicy.php b/app/Policies/CustomerPolicy.php new file mode 100644 index 0000000..e9253b2 --- /dev/null +++ b/app/Policies/CustomerPolicy.php @@ -0,0 +1,17 @@ +login($service->brand)->exists(); + } +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 797d739..aaddf7d 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -52,6 +52,7 @@ class AuthServiceProvider extends ServiceProvider try { $service = Service::getFromApiToken($apiToken); + $service = $service->load('brand'); app()->bind(Service::class, function () use ($service) { return $service; }); diff --git a/clients/V2 b/clients/V2 index eb69ef3..a336b3b 160000 --- a/clients/V2 +++ b/clients/V2 @@ -1 +1 @@ -Subproject commit eb69ef30e9d2512d26c9f2718ef4d7d054dd766b +Subproject commit a336b3bfca10f6d383e585d68e80cc85b1b7295d diff --git a/composer.json b/composer.json index 0205b55..399d656 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "ldc/ldccrmv2client", "type": "project", - "version": "2.0.0", + "version": "2.0.1", "description": "LDC CRM V2", "authors": [ { diff --git a/database/factories/AnswerFactory.php b/database/factories/AnswerFactory.php index 3564a18..b9dcbd0 100644 --- a/database/factories/AnswerFactory.php +++ b/database/factories/AnswerFactory.php @@ -2,8 +2,7 @@ namespace Database\Factories; -use App\Enums\QuestionType; -use App\Models\Question; +use App\Models\Answer; use Illuminate\Database\Eloquent\Factories\Factory; class AnswerFactory extends Factory @@ -14,7 +13,7 @@ class AnswerFactory extends Factory * * @var string */ - protected $model = Question::class; + protected $model = Answer::class; /** * Define the model's default state. @@ -24,8 +23,7 @@ class AnswerFactory extends Factory public function definition() { return [ - 'question' => $this->faker->name, - 'type' => collect(QuestionType::toArray())->random()->value, + 'answer' => $this->faker->realText(20), ]; } } diff --git a/database/factories/QuestionFactory.php b/database/factories/QuestionFactory.php index 42843a0..7004794 100644 --- a/database/factories/QuestionFactory.php +++ b/database/factories/QuestionFactory.php @@ -24,7 +24,7 @@ class QuestionFactory extends Factory public function definition() { return [ - 'question' => $this->faker->name, + 'question' => $this->faker->realText(40), 'type' => collect(QuestionType::toArray())->random()->value, ]; } diff --git a/database/migrations/2021_08_10_104859_create_customer_answer_table.php b/database/migrations/2021_08_10_104859_create_customer_answer_table.php index 6124faf..6f092c5 100644 --- a/database/migrations/2021_08_10_104859_create_customer_answer_table.php +++ b/database/migrations/2021_08_10_104859_create_customer_answer_table.php @@ -14,7 +14,7 @@ class CreateCustomerAnswerTable extends Migration public function up() { Schema::create('customer_answer', function (Blueprint $table) { - $table->foreignId('question_id')->constrained('questions'); + $table->foreignId('answer_id')->constrained('answers'); $table->foreignId('customer_id')->constrained('customers'); $table->timestamp('created_at')->useCurrent(); }); diff --git a/database/seeders/BrandSeeder.php b/database/seeders/BrandSeeder.php index f4ed875..f71cc8f 100644 --- a/database/seeders/BrandSeeder.php +++ b/database/seeders/BrandSeeder.php @@ -2,7 +2,9 @@ namespace Database\Seeders; +use App\Models\Answer; use App\Models\Brand; +use App\Models\Question; use App\Models\Service; use Illuminate\Database\Seeder; @@ -20,6 +22,14 @@ class BrandSeeder extends Seeder Service::factory(1)->make()->each( /** @var \App\Models\Service $service*/ function ($service) use ($brand) { + Question::factory(10)->make()->each(function ($question) use ($brand) { + $question->brand()->associate($brand); + $question->save(); + Answer::factory(rand(3, 4))->make()->each(function ($answer) use ($question) { + $answer->question()->associate($question); + $answer->save(); + }); + }); $service->brand()->associate($brand); $service->save(); } diff --git a/routes/api.php b/routes/api.php index 7355450..ed014ca 100644 --- a/routes/api.php +++ b/routes/api.php @@ -7,6 +7,8 @@ use App\Http\Controllers\Api\V2\ApiController as V2ApiController; use App\Http\Controllers\Api\V2\ArcadiaController as V2ArcadiaController; use App\Http\Controllers\Api\V2\CustomerController as V2CustomerController; use App\Http\Controllers\Api\V2\OptinController as V2OptinController; +use App\Http\Controllers\Api\V2\AnswerController as V2AnswerController; +use App\Http\Controllers\Api\V2\QuestionController as V2QuestionController; /* |-------------------------------------------------------------------------- @@ -43,11 +45,13 @@ Route::prefix('v2')->name('apiV2.')->group(function () { Route::post('/auth', [V2ApiController::class, 'authenticate'])->name('authenticate'); Route::middleware('auth:api')->group(function () { Route::post('/optin', [V2OptinController::class, 'optin'])->name('optin'); - Route::apiResource('customer', V2CustomerController::class)->except(['index']); - Route::prefix('customer')->name('customer.')->group(function () { + Route::apiResource('customers', V2CustomerController::class)->except(['index']); + Route::prefix('customers')->name('customers.')->group(function () { Route::post('/authenticate', [V2CustomerController::class, 'authenticate'])->name('authenticate'); Route::prefix('{customer}')->group(function () { Route::post('/activate', [V2CustomerController::class, 'activateAccount'])->name('activate'); + Route::post('/answers', [V2AnswerController::class, 'storeCustomerAnswers']) + ->name('answers.store'); }); }); Route::prefix('arcadia')->name('arcadia.')->group(function () { @@ -56,5 +60,12 @@ Route::prefix('v2')->name('apiV2.')->group(function () { Route::post('/outgoing-mail/{customer}/game', [V2ArcadiaController::class, 'outgoingMailGame']) ->name('outgoingMailGame'); }); + Route::apiResource('questions', V2QuestionController::class)->only(['index']); + Route::prefix('questions')->name('questions.')->group(function () { + Route::get('/un-answered-by/{customer}', [V2QuestionController::class, 'unAnsweredBy']) + ->name('unAnsweredBy'); + Route::get('/with-answers/{customer}', [V2QuestionController::class, 'indexWithResponse']) + ->name('indexWithResponse'); + }); }); }); diff --git a/tests/Feature/ApiV2/ApiBase.php b/tests/Feature/ApiV2/ApiBase.php index 545d5a6..a01c1cc 100644 --- a/tests/Feature/ApiV2/ApiBase.php +++ b/tests/Feature/ApiV2/ApiBase.php @@ -69,12 +69,16 @@ class ApiBase extends BaseTestCase * @param boolean $status the customer status for a brand * @return array */ - protected function getCustomer(bool $status = true): 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); - })->inRandomOrder()->firstOrFail(); + }); + if ($random) { + $customer = $customer->inRandomOrder(); + } + $customer = $customer->firstOrFail(); /** @var \App\Models\Login $login */ $login = $customer->logins()->where('status', $status)->firstOrFail(); /** @var \App\Models\Brand $brand */ diff --git a/tests/Feature/ApiV2/ArcadiaTest.php b/tests/Feature/ApiV2/ArcadiaTest.php index 38cdc72..e3aa138 100644 --- a/tests/Feature/ApiV2/ArcadiaTest.php +++ b/tests/Feature/ApiV2/ArcadiaTest.php @@ -14,7 +14,7 @@ class ArcadiaTest extends ApiBase use DatabaseTransactions; /** - * route: apiV2.customer.arcadia.outgoingMail + * route: apiV2.customers.arcadia.outgoingMail */ public function testArcadiaSendOrderDefault() { diff --git a/tests/Feature/ApiV2/CustomerTest.php b/tests/Feature/ApiV2/CustomerTest.php index 4fe3800..a22204d 100644 --- a/tests/Feature/ApiV2/CustomerTest.php +++ b/tests/Feature/ApiV2/CustomerTest.php @@ -34,7 +34,7 @@ class CustomerTest extends ApiBase } /** - * route: apiV2.customer.authenticate + * route: apiV2.customers.authenticate */ public function testApiAuthenticateCustomer() { @@ -42,7 +42,7 @@ class CustomerTest extends ApiBase $response = $this->callApiWithToken( $service, 'post', - route('apiV2.customer.authenticate', ['customer' => $info->hashcode]), + route('apiV2.customers.authenticate', ['customer' => $info->hashcode]), [ 'email' => $customer->email, 'password' => 'testPassword' @@ -65,7 +65,7 @@ class CustomerTest extends ApiBase } /** - * route: apiV2.customer.store + * route: apiV2.customers.store */ public function testApiStoreCustomer() { @@ -83,7 +83,7 @@ class CustomerTest extends ApiBase 'postcode' => '72160', 'opt_in' => true ]; - $response = $this->callApiWithToken($service, 'post', route('apiV2.customer.store'), $data); + $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $data); $json = $response->decodeResponseJson(); $this->assertApiResponseStatusIs($json, ApiStatus::OK()); @@ -106,7 +106,7 @@ class CustomerTest extends ApiBase $this->assertEquals($data['city'], $json->json('data.city')); $this->assertEquals($data['postcode'], $json->json('data.postcode')); - $response = $this->callApiWithToken($service, 'post', route('apiV2.customer.store'), $data); + $response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $data); $json = $response->decodeResponseJson(); $this->assertApiResponseStatusIs($json, ApiStatus::KO()); @@ -115,28 +115,28 @@ class CustomerTest extends ApiBase $data['password'] = 'password'; - $response = $this->callApiWithToken($service, 'post', route('apiV2.customer.store'), $data); + $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.customer.activate + * route: apiV2.customers.activate */ public function testActivateCustomer() { extract($this->getCustomer(false)); $response = $this->callApiWithToken($service, 'post', route( - 'apiV2.customer.activate', + 'apiV2.customers.activate', ['customer' => $info->hashcode] ), []); $json = $response->decodeResponseJson(); $this->assertApiResponseStatusIs($json, ApiStatus::OK()); $response = $this->callApiWithToken($service, 'post', route( - 'apiV2.customer.activate', + 'apiV2.customers.activate', ['customer' => $info->hashcode] ), []); $json = $response->decodeResponseJson(); @@ -146,7 +146,7 @@ class CustomerTest extends ApiBase } /** - * route: apiV2.customer.update + * route: apiV2.customers.update */ public function testApiUpdateCustomer() { @@ -167,7 +167,7 @@ class CustomerTest extends ApiBase foreach ($data as $rowName => $row) { $response = $this->callApiWithToken($service, 'patch', route( - 'apiV2.customer.update', + 'apiV2.customers.update', ['customer' => $info->hashcode] ), [$rowName => $row]); $json = $response->decodeResponseJson(); @@ -189,7 +189,7 @@ class CustomerTest extends ApiBase } } $response = $this->callApiWithToken($service, 'patch', route( - 'apiV2.customer.update', + 'apiV2.customers.update', ['customer' => $info->hashcode] ), ['address2' => 'anything']); $json = $response->decodeResponseJson(); @@ -198,14 +198,14 @@ class CustomerTest extends ApiBase } /** - * route: apiV2.customer.show + * route: apiV2.customers.show */ public function testApiGetCustomer() { extract($this->getCustomer()); $response = $this->callApiWithToken($service, 'get', route( - 'apiV2.customer.show', + 'apiV2.customers.show', ['customer' => $info->hashcode] )); $json = $response->decodeResponseJson(); @@ -226,7 +226,7 @@ class CustomerTest extends ApiBase $uuid = Str::uuid(); $response = $this->callApiWithToken($service, 'get', route( - 'apiV2.customer.show', + 'apiV2.customers.show', ['customer' => $uuid] )); $json = $response->decodeResponseJson(); @@ -252,7 +252,7 @@ class CustomerTest extends ApiBase extract($this->getCustomer()); $service = $brand->services()->firstOrFail(); $response = $this->callApiWithToken($service, 'delete', route( - 'apiV2.customer.destroy', + 'apiV2.customers.destroy', ['customer' => $info->hashcode] )); $json = $response->decodeResponseJson(); @@ -260,7 +260,7 @@ class CustomerTest extends ApiBase $this->assertEmpty($json->json('data')); $response = $this->callApiWithToken($service, 'get', route( - 'apiV2.customer.show', + 'apiV2.customers.show', ['customer' => $info->hashcode] )); $json = $response->decodeResponseJson(); diff --git a/tests/Feature/ApiV2/QuestionTest.php b/tests/Feature/ApiV2/QuestionTest.php new file mode 100644 index 0000000..4e072db --- /dev/null +++ b/tests/Feature/ApiV2/QuestionTest.php @@ -0,0 +1,72 @@ +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/SingleOptinTest.php b/tests/Feature/ApiV2/SingleOptinTest.php index 6ac1e4c..2b8a035 100644 --- a/tests/Feature/ApiV2/SingleOptinTest.php +++ b/tests/Feature/ApiV2/SingleOptinTest.php @@ -68,6 +68,6 @@ class SingleOptinTest extends ApiBase 'postcode' => '72160', 'opt_in' => true ]; - $this->callApiWithToken($service, 'post', route('apiV2.customer.store'), $data); + $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $data); } } diff --git a/tests/Unit/CrmClientV2CustomerTest.php b/tests/Unit/CrmClientV2CustomerTest.php index 0bbe892..f2a2c9a 100644 --- a/tests/Unit/CrmClientV2CustomerTest.php +++ b/tests/Unit/CrmClientV2CustomerTest.php @@ -251,11 +251,11 @@ class CrmClientV2CustomerTest extends CrmClientTester public function testCustomerDelete() { $api = new CrmApiV2(); - $customer = $api->getCustomerOrNew(static::$info->hashcode); + $customer = $api->getCustomer('toto.delarogerie@gmail.com'); $this->assertTrue($customer->delete()); - $customer = $api->getCustomerOrNew(static::$info->hashcode); + $customer = $api->getCustomerOrNew('toto.delarogerie@gmail.com'); $this->assertEquals(null, $customer->hashcode); } } diff --git a/tests/Unit/CrmClientV2QuestionTest.php b/tests/Unit/CrmClientV2QuestionTest.php new file mode 100644 index 0000000..1b40289 --- /dev/null +++ b/tests/Unit/CrmClientV2QuestionTest.php @@ -0,0 +1,75 @@ +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/CrmClientV2RouterTest.php b/tests/Unit/CrmClientV2RouterTest.php index 7059f01..c4e9321 100644 --- a/tests/Unit/CrmClientV2RouterTest.php +++ b/tests/Unit/CrmClientV2RouterTest.php @@ -36,6 +36,12 @@ class CrmClientV2RouterTest extends CrmClientTester if (\strpos($route['uri'], '{customer}') !== false) { $query['customer'] = 1; } + if (\strpos($route['uri'], '{question}') !== false) { + $query['question'] = 1; + } + if (\strpos($route['uri'], '{answer}') !== false) { + $query['answer'] = 1; + } $request = Utils::getResquest($name, $query, $data); $request->uri; $request->method;