Skip to content
This repository was archived by the owner on Jun 29, 2021. It is now read-only.

Resolves #27 - Update question #41

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ All notable changes to `laravel-portugal/api` will be documented in this file
- First version of the API documentation
- A guest should be able to login and logout (#37)
- An authenticated user can post an answer to a question (#31)
- An authenticated user can update a question (#27)

### Changed

Expand Down
37 changes: 37 additions & 0 deletions domains/Discussions/Controllers/QuestionsUpdateController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Domains\Discussions\Controllers;

use App\Http\Controllers\Controller;
use Domains\Discussions\Models\Question;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class QuestionsUpdateController extends Controller
{
private Question $questions;

public function __construct(Question $questions)
{
$this->questions = $questions;
}

public function __invoke(int $questionId, Request $request): Response
{
$question = $this->questions->findOrFail($questionId);

$this->authorize('update', $question);

$this->validate($request, [
'title' => ['required', 'string', 'max:255'],
'description' => ['nullable', 'string'],
]);

$question->update([
'title' => $request->input('title'),
'description' => $request->input('description', $question->description),
]);

return new Response('', Response::HTTP_NO_CONTENT);
}
}
8 changes: 8 additions & 0 deletions domains/Discussions/DiscussionsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use Domains\Discussions\Models\Question;
use Domains\Discussions\Observers\QuestionObserver;
use Domains\Discussions\Policies\QuestionPolicy;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;

Expand All @@ -14,6 +16,7 @@ public function boot(): void
$this->loadMigrationsFrom(__DIR__ . '/Database/Migrations');
$this->bootRoutes();
$this->bootObservers();
$this->bootPolicies();
}

private function bootRoutes(): void
Expand All @@ -31,4 +34,9 @@ private function bootObservers(): void
{
Question::observe(QuestionObserver::class);
}

private function bootPolicies(): void
{
Gate::policy(Question::class, QuestionPolicy::class);
}
}
2 changes: 2 additions & 0 deletions domains/Discussions/Models/Question.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class Question extends Model
{
use SoftDeletes;

protected $fillable = ['title', 'description'];

public function author(): BelongsTo
{
return $this->belongsTo(User::class)
Expand Down
17 changes: 17 additions & 0 deletions domains/Discussions/Policies/QuestionPolicy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Domains\Discussions\Policies;

use Domains\Accounts\Models\User;
use Domains\Discussions\Models\Question;
use Illuminate\Auth\Access\HandlesAuthorization;

class QuestionPolicy
{
use HandlesAuthorization;

public function update(User $user, Question $question)
{
return $question->author->is($user);
}
}
103 changes: 103 additions & 0 deletions domains/Discussions/Tests/Feature/QuestionsUpdateTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace Domains\Discussions\Tests\Feature;

use Domains\Accounts\Database\Factories\UserFactory;
use Domains\Accounts\Models\User;
use Domains\Discussions\Database\Factories\QuestionFactory;
use Domains\Discussions\Models\Question;
use Faker\Factory;
use Faker\Generator;
use Illuminate\Http\Response;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
use Laravel\Lumen\Testing\DatabaseMigrations;
use Tests\TestCase;

class QuestionsUpdateTest extends TestCase
{
use DatabaseMigrations;

private Generator $faker;
private User $user;
private Question $question;

protected function setUp(): void
{
parent::setUp();

$this->faker = Factory::create();
$this->user = UserFactory::new()->create();
$this->question = QuestionFactory::new(['author_id' => $this->user->id])->create();
}

/** @test */
public function it_updates_questions(): void
{
Carbon::setTestNow();

$payload = [
'title' => $this->faker->title,
'description' => $this->faker->paragraph,
];

$response = $this->actingAs($this->user)
->call(
'PATCH',
route('discussions.questions.update', ['questionId' => $this->question->id]),
$payload
);

$this->assertResponseStatus(Response::HTTP_NO_CONTENT);

self::assertTrue($response->isEmpty());

$this->seeInDatabase('questions', [
'id' => $this->question->id,
'author_id' => $this->user->id,
'title' => $payload['title'],
'slug' => Str::slug($payload['title']),
'description' => $payload['description'],
'updated_at' => Carbon::now(),
]);
}

/** @test */
public function it_fails_to_update_if_title_is_missing(): void
{
$this->actingAs($this->user)
->patch(route('discussions.questions.update', ['questionId' => $this->question->id]))
->seeJsonStructure([
'title',
]);
}

/** @test */
public function it_keeps_previous_description_if_none_is_sent(): void
{
$response = $this->actingAs($this->user)
->call(
'PATCH',
route('discussions.questions.update', ['questionId' => $this->question->id]),
[
'title' => $this->faker->title,
]
);

self::assertTrue($response->isEmpty());
self::assertEquals($this->question->description, $this->question->refresh()->description);
}

/** @test */
public function it_forbids_non_owner_to_update_questions(): void
{
$this->actingAs(UserFactory::new()->make())
->patch(
route('discussions.questions.update', ['questionId' => $this->question->id]),
[
'title' => $this->faker->title,
]
)
->assertResponseStatus(Response::HTTP_FORBIDDEN);
}
}
27 changes: 17 additions & 10 deletions domains/Discussions/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@

use Domains\Discussions\Controllers\AnswersStoreController;
use Domains\Discussions\Controllers\QuestionsStoreController;
use Domains\Discussions\Controllers\QuestionsUpdateController;
use Illuminate\Support\Facades\Route;

Route::post('/questions', [
'as' => 'questions.store',
'middleware' => 'auth',
'uses' => QuestionsStoreController::class,
]);
Route::group(['middleware' => 'auth'], function () {
Route::post('/questions', [
'as' => 'questions.store',
'uses' => QuestionsStoreController::class,
]);

Route::patch('/questions/{questionId}', [
'as' => 'questions.update',
'uses' => QuestionsUpdateController::class,
]);

Route::post('/questions/{questionId}/answers', [
'as' => 'questions.answers',
'uses' => AnswersStoreController::class
]);
});

Route::post('/questions/{questionId}/answers', [
'as' => 'questions.answers',
'middleware' => 'auth',
'uses' => AnswersStoreController::class
]);
1 change: 1 addition & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<directory suffix=".php">./domains/*/Database</directory>
<directory suffix=".php">./domains/*/Routes</directory>
<directory suffix=".php">./domains/*/Tests</directory>
<directory suffix="blade.php">./domains/*/Resources</directory>
<directory suffix=".php">./tests</directory>
</exclude>
</coverage>
Expand Down