diff --git a/app/Http/Controllers/Api/V1/InvitationController.php b/app/Http/Controllers/Api/V1/InvitationController.php index 0786c72d..654628de 100644 --- a/app/Http/Controllers/Api/V1/InvitationController.php +++ b/app/Http/Controllers/Api/V1/InvitationController.php @@ -60,7 +60,7 @@ class InvitationController extends Controller $this->user(), $organization, $request->input('email'), - $request->input('role') + $request->getRole()->value ); return response()->json(null, 204); diff --git a/app/Http/Controllers/Api/V1/MemberController.php b/app/Http/Controllers/Api/V1/MemberController.php index b0eaf22a..c5eab462 100644 --- a/app/Http/Controllers/Api/V1/MemberController.php +++ b/app/Http/Controllers/Api/V1/MemberController.php @@ -68,7 +68,7 @@ class MemberController extends Controller $member->billable_rate = $request->getBillableRate(); } if ($request->has('role')) { - $member->role = $request->input('role'); + $member->role = $request->getRole()->value; } $member->save(); diff --git a/app/Http/Requests/V1/Invitation/InvitationStoreRequest.php b/app/Http/Requests/V1/Invitation/InvitationStoreRequest.php index dc02996a..9f479bc8 100644 --- a/app/Http/Requests/V1/Invitation/InvitationStoreRequest.php +++ b/app/Http/Requests/V1/Invitation/InvitationStoreRequest.php @@ -30,9 +30,14 @@ class InvitationStoreRequest extends FormRequest 'role' => [ 'required', 'string', - // TODO: placeholder role should not be allowed - Rule::enum(Role::class), + Rule::enum(Role::class) + ->except([Role::Owner, Role::Placeholder]), ], ]; } + + public function getRole(): Role + { + return Role::from($this->input('role')); + } } diff --git a/app/Http/Requests/V1/Member/MemberUpdateRequest.php b/app/Http/Requests/V1/Member/MemberUpdateRequest.php index eaaa1bb4..e7defb67 100644 --- a/app/Http/Requests/V1/Member/MemberUpdateRequest.php +++ b/app/Http/Requests/V1/Member/MemberUpdateRequest.php @@ -25,8 +25,8 @@ class MemberUpdateRequest extends FormRequest return [ 'role' => [ 'string', - // TODO: placeholder role should not be allowed - Rule::enum(Role::class), + Rule::enum(Role::class) + ->except([Role::Owner, Role::Placeholder]), ], 'billable_rate' => [ 'nullable', @@ -52,4 +52,9 @@ class MemberUpdateRequest extends FormRequest return $this->has('billable_rate_update_time_entries') && $this->input('billable_rate_update_time_entries') === 'true'; } + + public function getRole(): Role + { + return Role::from($this->input('role')); + } } diff --git a/tests/Unit/Endpoint/Api/V1/InvitationEndpointTest.php b/tests/Unit/Endpoint/Api/V1/InvitationEndpointTest.php index c0bc6c23..00209a94 100644 --- a/tests/Unit/Endpoint/Api/V1/InvitationEndpointTest.php +++ b/tests/Unit/Endpoint/Api/V1/InvitationEndpointTest.php @@ -57,8 +57,7 @@ class InvitationEndpointTest extends ApiEndpointTestAbstract public function test_store_fails_if_user_has_no_permission_to_create_invitations(): void { // Arrange - $data = $this->createUserWithPermission([ - ]); + $data = $this->createUserWithPermission(); Passport::actingAs($data->user); // Act @@ -71,6 +70,44 @@ class InvitationEndpointTest extends ApiEndpointTestAbstract $response->assertStatus(403); } + public function test_store_fails_if_user_invites_with_role_owner(): void + { + // Arrange + $data = $this->createUserWithPermission([ + 'invitations:create', + ]); + Passport::actingAs($data->user); + + // Act + $response = $this->postJson(route('api.v1.invitations.store', $data->organization->getKey()), [ + 'email' => 'test@asdf.at', + 'role' => Role::Owner->value, + ]); + + // Assert + $response->assertStatus(422); + $response->assertJsonPath('message', 'The selected role is invalid.'); + } + + public function test_store_fails_if_user_invites_with_role_placeholder(): void + { + // Arrange + $data = $this->createUserWithPermission([ + 'invitations:create', + ]); + Passport::actingAs($data->user); + + // Act + $response = $this->postJson(route('api.v1.invitations.store', $data->organization->getKey()), [ + 'email' => 'test@asdf.at', + 'role' => Role::Placeholder->value, + ]); + + // Assert + $response->assertStatus(422); + $response->assertJsonPath('message', 'The selected role is invalid.'); + } + public function test_store_invites_user_to_organization(): void { // Arrange diff --git a/tests/Unit/Endpoint/Api/V1/MemberEndpointTest.php b/tests/Unit/Endpoint/Api/V1/MemberEndpointTest.php index 3be1142e..79c1e56d 100644 --- a/tests/Unit/Endpoint/Api/V1/MemberEndpointTest.php +++ b/tests/Unit/Endpoint/Api/V1/MemberEndpointTest.php @@ -136,6 +136,63 @@ class MemberEndpointTest extends ApiEndpointTestAbstract $this->assertSame(10001, $member->billable_rate); } + public function test_update_member_can_update_role(): void + { + // Arrange + $data = $this->createUserWithPermission([ + 'members:update', + ]); + $otherUser = User::factory()->create(); + $otherMember = Member::factory()->forUser($otherUser)->forOrganization($data->organization)->role(Role::Employee)->create(); + Passport::actingAs($data->user); + + // Act + $response = $this->putJson(route('api.v1.members.update', [$data->organization->getKey(), $otherMember->getKey()]), [ + 'role' => Role::Admin->value, + ]); + + // Assert + $response->assertStatus(200); + $otherMember->refresh(); + $this->assertSame(Role::Admin->value, $otherMember->role); + } + + public function test_update_member_role_fails_if_role_is_owner(): void + { + // Arrange + $data = $this->createUserWithPermission([ + 'members:update', + ]); + Passport::actingAs($data->user); + + // Act + $response = $this->putJson(route('api.v1.members.update', [$data->organization->getKey(), $data->member->getKey()]), [ + 'role' => Role::Owner->value, + ]); + + // Assert + $response->assertStatus(422); + $response->assertJsonPath('message', 'The selected role is invalid.'); + } + + public function test_update_member_role_fails_if_role_is_placeholder(): void + { + // Arrange + $data = $this->createUserWithPermission([ + 'members:update', + ]); + Passport::actingAs($data->user); + + // Act + $response = $this->putJson(route('api.v1.members.update', [$data->organization->getKey(), $data->member->getKey()]), [ + 'role' => Role::Placeholder->value, + ]); + + // Assert + $response->assertStatus(422); + $response->assertJsonPath('message', 'The selected role is invalid.'); + } + public function test_invite_placeholder_succeeds_if_data_is_valid(): void { $data = $this->createUserWithPermission([