Skip to content

Commit b8ff405

Browse files
committed
feat: implement role synchronization and update roles endpoint in UserIdentityController
1 parent b23943f commit b8ff405

File tree

8 files changed

+161
-0
lines changed

8 files changed

+161
-0
lines changed

contexts/Authorization/Application/Coordinators/UserIdentityCoordinator.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use Contexts\Authorization\Domain\UserIdentity\Models\UserStatus;
1717
use Contexts\Authorization\Infrastructure\Repositories\UserRepository;
1818
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
19+
use Contexts\Authorization\Domain\UserIdentity\Models\RoleIdCollection;
20+
use Contexts\Authorization\Domain\Role\Models\RoleId;
1921

2022
class UserIdentityCoordinator extends BaseCoordinator
2123
{
@@ -91,4 +93,17 @@ public function changePassword(int $userId, string $newPassword)
9193

9294
$this->repository->changePassword($user);
9395
}
96+
97+
public function syncRoles(int $userId, array $roleIds): void
98+
{
99+
$newRoles = new RoleIdCollection(
100+
array_map(fn ($id) => RoleId::fromInt($id), $roleIds)
101+
);
102+
103+
$user = $this->repository->getById(UserId::fromInt($userId));
104+
105+
$user->syncRoles($newRoles);
106+
107+
$this->repository->update($user);
108+
}
94109
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Contexts\Authorization\Domain\UserIdentity\Events;
6+
7+
use Contexts\Authorization\Domain\Role\Models\RoleId;
8+
use Contexts\Authorization\Domain\UserIdentity\Models\UserId;
9+
use Illuminate\Foundation\Events\Dispatchable;
10+
11+
class RoleAssignedEvent
12+
{
13+
use Dispatchable;
14+
15+
public function __construct(
16+
private readonly UserId $userId,
17+
private readonly RoleId $roleId
18+
) {
19+
}
20+
21+
public function getUserId(): UserId
22+
{
23+
return $this->userId;
24+
}
25+
26+
public function getRoleId(): RoleId
27+
{
28+
return $this->roleId;
29+
}
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Contexts\Authorization\Domain\UserIdentity\Events;
6+
7+
use Contexts\Authorization\Domain\Role\Models\RoleId;
8+
use Contexts\Authorization\Domain\UserIdentity\Models\UserId;
9+
use Illuminate\Foundation\Events\Dispatchable;
10+
11+
class RoleRemovedEvent
12+
{
13+
use Dispatchable;
14+
15+
public function __construct(
16+
private readonly UserId $userId,
17+
private readonly RoleId $roleId
18+
) {
19+
}
20+
21+
public function getUserId(): UserId
22+
{
23+
return $this->userId;
24+
}
25+
26+
public function getRoleId(): RoleId
27+
{
28+
return $this->roleId;
29+
}
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Illuminate\Database\Migrations\Migration;
6+
use Illuminate\Database\Schema\Blueprint;
7+
use Illuminate\Support\Facades\Schema;
8+
9+
return new class () extends Migration {
10+
/**
11+
* Run the migrations.
12+
*/
13+
public function up(): void
14+
{
15+
Schema::create('pivot_user_role', function (Blueprint $table) {
16+
$table->increments('id');
17+
$table->unsignedInteger('user_id');
18+
$table->unsignedInteger('role_id');
19+
$table->timestamps();
20+
});
21+
}
22+
23+
/**
24+
* Reverse the migrations.
25+
*/
26+
public function down(): void
27+
{
28+
Schema::dropIfExists('roles');
29+
}
30+
};

contexts/Authorization/Infrastructure/Routes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
Route::get('', 'getUserList')->name('getUserList');
1313
Route::post('', 'createUser')->name('createUser');
1414
Route::patch('{id}/password', 'changePassword')->name('changePassword');
15+
Route::put('{id}/roles', 'updateRoles')->name('updateRoles');
1516
Route::put('{id}/subspend', 'subspendUser')->name('subspendUser');
1617
Route::put('{id}', 'updateUser')->name('updateUser');
1718
Route::delete('{id}', 'deleteUser')->name('deleteUser');

contexts/Authorization/Presentation/Controllers/UserIdentityController.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Contexts\Authorization\Presentation\Requests\User\UpdateUserRequest;
1616
use Contexts\Authorization\Presentation\Requests\User\UserIdRequest;
1717
use Contexts\Authorization\Presentation\Resources\UserResource;
18+
use Contexts\Authorization\Presentation\Requests\User\UpdateRolesRequest;
1819

1920
class UserIdentityController extends BaseController
2021
{
@@ -90,4 +91,15 @@ public function changePassword(ChangePasswordRequest $request)
9091
->message('Password changed successfully')
9192
->send();
9293
}
94+
95+
public function updateRoles(UpdateRolesRequest $request)
96+
{
97+
$data = $request->validated();
98+
$userId = (int) ($data['id']);
99+
app(UserIdentityCoordinator::class)->syncRoles($userId, $data['role_ids']);
100+
101+
return $this->success()
102+
->message('Roles updated successfully')
103+
->send();
104+
}
93105
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Contexts\Authorization\Presentation\Requests\User;
6+
7+
use App\Http\Requests\BaseFormRequest;
8+
9+
class UpdateRolesRequest extends BaseFormRequest
10+
{
11+
public function rules(): array
12+
{
13+
return [
14+
'id' => $this->idRule(),
15+
'role_ids' => ['required', 'array'],
16+
'role_ids.*' => ['required', 'integer', 'gt:0'],
17+
];
18+
}
19+
}

contexts/Authorization/Tests/Feature/UserTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
declare(strict_types=1);
44

5+
use Contexts\Authorization\Infrastructure\Records\RoleRecord;
6+
57
it('can create active users via api', function () {
68
$response = $this->postJson('users', [
79
'email' => 'test@email.com',
@@ -142,3 +144,25 @@
142144

143145
$response->assertStatus(200);
144146
});
147+
148+
it('can update user roles via api', function () {
149+
$response = $this->postJson('users', [
150+
'email' => 'test@email.com',
151+
'password' => 'password123',
152+
'display_name' => 'My User',
153+
'status' => 'active',
154+
]);
155+
156+
$response->assertStatus(201);
157+
158+
$id = (int) $response->json('data.id');
159+
160+
$roles = RoleRecord::factory()->count(2)->create();
161+
$roleIds = $roles->pluck('id')->toArray();
162+
163+
$response = $this->putJson("users/{$id}/roles", [
164+
'role_ids' => $roleIds
165+
]);
166+
167+
$response->assertStatus(200);
168+
});

0 commit comments

Comments
 (0)