Skip to content

Commit

Permalink
feat: account deactivation
Browse files Browse the repository at this point in the history
  • Loading branch information
muhammadmp97 committed Sep 10, 2023
1 parent 56d6399 commit e0db817
Show file tree
Hide file tree
Showing 16 changed files with 230 additions and 7 deletions.
4 changes: 4 additions & 0 deletions app/Actions/User/LoginUserAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Actions\User;

use App\Models\DeactivationRequest;
use App\Models\User;
use Illuminate\Support\Facades\Hash;

Expand All @@ -15,6 +16,9 @@ public function execute(array $data): string
abort(401, 'User not found!');
}

// Logging into an account could save it from deletion
DeactivationRequest::where('user_id', $user->id)->delete();

return $user
->createToken(request()->header('User-Agent', 'Unkown User Agent'))
->plainTextToken;
Expand Down
16 changes: 16 additions & 0 deletions app/Actions/User/RequestDeactivationAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace App\Actions\User;

use App\Models\DeactivationRequest;
use App\Models\User;

class RequestDeactivationAction
{
public function execute(User $user): void
{
DeactivationRequest::create([
'user_id' => $user->id,
]);
}
}
35 changes: 35 additions & 0 deletions app/Console/Commands/HandleDeactivationRequests.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace App\Console\Commands;

use App\Models\DeactivationRequest;
use App\Models\User;
use Illuminate\Console\Command;

class HandleDeactivationRequests extends Command
{
protected $signature = 'app:handle-deactivation-requests';

protected $description = 'Handles deactivation requests';

public function handle()
{
$userIds = DeactivationRequest::query()
->where('created_at', '<', now()->subDays(10))
->pluck('user_id');

$users = User::query()
->whereIn('id', $userIds)
->get();

foreach ($users as $user) {
$user->delete();
}

DeactivationRequest::query()
->whereIn('user_id', $userIds)
->delete();

return $this->comment('Done!');
}
}
2 changes: 2 additions & 0 deletions app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ protected function schedule(Schedule $schedule): void
$schedule->command('sanctum:prune-expired --hours=24')->daily();

$schedule->command('app:close-abandoned-challenges')->hourly();

$schedule->command('app:handle-deactivation-requests')->daily();
}

/**
Expand Down
23 changes: 23 additions & 0 deletions app/Http/Controllers/DeactivationRequestsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Http\Controllers;

use App\Actions\User\LogoutUserAction;
use App\Actions\User\RequestDeactivationAction;

class DeactivationRequestsController extends Controller
{
public function __construct()
{
$this->middleware('auth:sanctum');
}

public function store(RequestDeactivationAction $requestDeactivationAction, LogoutUserAction $logoutUserAction)
{
$requestDeactivationAction->execute(request()->user());

$logoutUserAction->execute(request()->user());

$this->ok();
}
}
10 changes: 10 additions & 0 deletions app/Models/DeactivationRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class DeactivationRequest extends Model
{
protected $guarded = [];
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public function up(): void
{
Schema::create('challenges', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->enum('status', ['ongoing', 'stopped', 'abandoned', 'completed'])->default('ongoing');
$table->string('text')->nullable();
$table->timestamp('continued_at')->nullable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public function up(): void
{
Schema::create('likes', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id');
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->morphs('likable');
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public function up(): void
{
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id');
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->morphs('commentable');
$table->string('text');
$table->timestamps();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ public function up(): void
{
Schema::create('followers', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(User::class, 'follower_id');
$table->foreignIdFor(User::class, 'following_id');
$table->foreignIdFor(User::class, 'follower_id')->constrained()->cascadeOnDelete();
$table->foreignIdFor(User::class, 'following_id')->constrained()->cascadeOnDelete();
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public function up(): void
{
Schema::create('reports', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(User::class, 'reporter_id')->nullable();
$table->foreignIdFor(User::class, 'reporter_id')->constrained()->cascadeOnDelete();
$table->morphs('reportable');
$table->string('text');
$table->boolean('is_closed')->default(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public function up(): void
{
Schema::create('user_achievements', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id');
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->unsignedTinyInteger('achievement_id');
$table->timestamps();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('deactivation_requests', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->unique();
$table->timestamps();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('deactivation_requests');
}
};
3 changes: 3 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Http\Controllers\ChallengeLikesController;
use App\Http\Controllers\ChallengesController;
use App\Http\Controllers\ContinueChallengeController;
use App\Http\Controllers\DeactivationRequestsController;
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\ReportsController;
use App\Http\Controllers\UserAchievementsController;
Expand All @@ -22,6 +23,8 @@
Route::post('change-password', [AuthController::class, 'changePassword']);
});

Route::post('deactivate', [DeactivationRequestsController::class, 'store']);

Route::get('profile', [ProfileController::class, 'index']);
Route::patch('profile', [ProfileController::class, 'update']);

Expand Down
41 changes: 41 additions & 0 deletions tests/Feature/DeactivationRequestsControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Tests\Feature;

use App\Models\Country;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;

class DeactivationRequestsControllerTest extends TestCase
{
use RefreshDatabase;

private $user;

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

Country::create([
'code' => 'GB',
'name' => 'United Kingdom',
]);

$this->user = $this->signIn();
}

public function test_user_requests_account_deactivation()
{
Sanctum::actingAs($this->user);
$this->user->currentAccessToken()->shouldReceive('delete')->once();

$this
->postJson('api/deactivate')
->assertOk();

$this->assertDatabaseHas('deactivation_requests', [
'user_id' => $this->user->id,
]);
}
}
61 changes: 61 additions & 0 deletions tests/Unit/Commands/HandleDeactivationRequestsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace Tests\Unit;

use App\Actions\User\RequestDeactivationAction;
use App\Models\Challenge;
use App\Models\Country;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class HandleDeactivationRequestsTest extends TestCase
{
use RefreshDatabase;

private User $user;

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

Country::create([
'code' => 'GB',
'name' => 'United Kingdom',
]);

$this->user = $this->signIn();
}

public function test_deletes_user_data(): void
{
Challenge::factory()->create();

Challenge::query()
->first()
->comments()
->create([
'user_id' => 1,
'text' => 'Keep fighting, dude!',
]);

Challenge::query()
->first()
->likes()
->create([
'user_id' => 1,
]);

app(RequestDeactivationAction::class)->execute($this->user);

$this->travelTo(now()->addDays(11));

$this->artisan('app:handle-deactivation-requests');

$this->assertDatabaseCount('deactivation_requests', 0);
$this->assertDatabaseCount('users', 0);
$this->assertDatabaseCount('challenges', 0);
$this->assertDatabaseCount('comments', 0);
$this->assertDatabaseCount('likes', 0);
}
}

0 comments on commit e0db817

Please sign in to comment.