Skip to content

Commit 453cb1d

Browse files
committed
Update starter kit to use Scramble for OpenAPI docs
1 parent 6f9a28f commit 453cb1d

11 files changed

Lines changed: 82 additions & 260 deletions

File tree

kits/API/Teams/app/Http/Controllers/Auth/RegisterController.php

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,19 @@
77
use App\Http\Requests\Auth\RegisterRequest;
88
use App\Http\Resources\UserResource;
99
use App\Models\User;
10+
use Dedoc\Scramble\Attributes\Endpoint;
11+
use Dedoc\Scramble\Attributes\Group;
1012
use Illuminate\Auth\Events\Registered;
1113
use Illuminate\Http\JsonResponse;
1214
use Illuminate\Http\Response;
1315
use Illuminate\Support\Facades\DB;
14-
use Knuckles\Scribe\Attributes\BodyParam;
15-
use Knuckles\Scribe\Attributes\Endpoint;
16-
use Knuckles\Scribe\Attributes\Group;
17-
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
1816

1917
#[Group('Authentication')]
2018
class RegisterController extends Controller
2119
{
2220
public function __construct(protected CreateTeam $createTeam) {}
2321

24-
#[Endpoint('Register', 'Create a new user account, provision a personal team, and return an API token.')]
25-
#[BodyParam('password_confirmation', 'string', required: true, description: 'Must match the password field.', example: 'password')]
26-
#[ResponseFromApiResource(UserResource::class, User::class, status: Response::HTTP_CREATED, additional: ['meta' => ['token' => 'YOUR_AUTH_TOKEN']])]
22+
#[Endpoint(title: 'Register', description: 'Create a new user account, provision a personal team, and return an API token.')]
2723
public function __invoke(RegisterRequest $request): JsonResponse
2824
{
2925
[$user, $token] = DB::transaction(function () use ($request): array {
@@ -37,6 +33,7 @@ public function __invoke(RegisterRequest $request): JsonResponse
3733
event(new Registered($user));
3834

3935
return (new UserResource($user))
36+
->ignoreFieldsAndIncludesInQueryString()
4037
->additional(['meta' => ['token' => $token]])
4138
->response()
4239
->setStatusCode(Response::HTTP_CREATED);

kits/API/Teams/app/Http/Controllers/Teams/AcceptTeamInvitationController.php

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,17 @@
66
use App\Http\Requests\Teams\AcceptTeamInvitationRequest;
77
use App\Http\Responses\MessageResponse;
88
use App\Models\TeamInvitation;
9+
use Dedoc\Scramble\Attributes\Endpoint;
10+
use Dedoc\Scramble\Attributes\Group;
11+
use Dedoc\Scramble\Attributes\Response as ScrambleResponse;
912
use Illuminate\Http\Response;
1013
use Illuminate\Support\Facades\DB;
11-
use Knuckles\Scribe\Attributes\Authenticated;
12-
use Knuckles\Scribe\Attributes\Endpoint;
13-
use Knuckles\Scribe\Attributes\Group;
14-
use Knuckles\Scribe\Attributes\Response as ScribeResponse;
1514

1615
#[Group('Team Invitations')]
17-
#[Authenticated]
1816
class AcceptTeamInvitationController extends Controller
1917
{
20-
#[Endpoint('Accept an invitation', 'Accept a pending team invitation for the authenticated user.')]
21-
#[ScribeResponse(['message' => 'Invitation accepted successfully.'], description: 'Invitation accepted')]
22-
#[ScribeResponse(status: Response::HTTP_UNPROCESSABLE_ENTITY, description: 'Invitation already accepted, expired, or sent to a different email address.')]
18+
#[Endpoint(title: 'Accept an invitation', description: 'Accept a pending team invitation for the authenticated user.')]
19+
#[ScrambleResponse(Response::HTTP_OK, 'Invitation accepted.', type: 'array{message: string}')]
2320
public function __invoke(AcceptTeamInvitationRequest $request, TeamInvitation $invitation): MessageResponse
2421
{
2522
$user = $request->user();

kits/API/Teams/app/Http/Controllers/Teams/DeclineTeamInvitationController.php

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,16 @@
66
use App\Http\Requests\Teams\AcceptTeamInvitationRequest;
77
use App\Http\Responses\MessageResponse;
88
use App\Models\TeamInvitation;
9+
use Dedoc\Scramble\Attributes\Endpoint;
10+
use Dedoc\Scramble\Attributes\Group;
11+
use Dedoc\Scramble\Attributes\Response as ScrambleResponse;
912
use Illuminate\Http\Response;
10-
use Knuckles\Scribe\Attributes\Authenticated;
11-
use Knuckles\Scribe\Attributes\Endpoint;
12-
use Knuckles\Scribe\Attributes\Group;
13-
use Knuckles\Scribe\Attributes\Response as ScribeResponse;
1413

1514
#[Group('Team Invitations')]
16-
#[Authenticated]
1715
class DeclineTeamInvitationController extends Controller
1816
{
19-
#[Endpoint('Decline an invitation', 'Decline a pending team invitation for the authenticated user.')]
20-
#[ScribeResponse(['message' => 'Invitation declined successfully.'], description: 'Invitation declined')]
21-
#[ScribeResponse(status: Response::HTTP_UNPROCESSABLE_ENTITY, description: 'Invitation already accepted, expired, or sent to a different email address.')]
17+
#[Endpoint(title: 'Decline an invitation', description: 'Decline a pending team invitation for the authenticated user.')]
18+
#[ScrambleResponse(Response::HTTP_OK, 'Invitation declined.', type: 'array{message: string}')]
2219
public function __invoke(AcceptTeamInvitationRequest $request, TeamInvitation $invitation): MessageResponse
2320
{
2421
$invitation->delete();

kits/API/Teams/app/Http/Controllers/Teams/TeamController.php

Lines changed: 13 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -11,104 +11,56 @@
1111
use App\Http\Resources\TeamInvitationResource;
1212
use App\Http\Resources\TeamResource;
1313
use App\Models\Team;
14+
use Dedoc\Scramble\Attributes\Endpoint;
15+
use Dedoc\Scramble\Attributes\Group;
1416
use Illuminate\Http\JsonResponse;
1517
use Illuminate\Http\Request;
1618
use Illuminate\Http\Response;
1719
use Illuminate\Support\Facades\DB;
1820
use Illuminate\Support\Facades\Gate;
19-
use Knuckles\Scribe\Attributes\Authenticated;
20-
use Knuckles\Scribe\Attributes\Endpoint;
21-
use Knuckles\Scribe\Attributes\Group;
22-
use Knuckles\Scribe\Attributes\Response as ScribeResponse;
23-
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
2421

2522
#[Group('Teams')]
26-
#[Authenticated]
2723
class TeamController extends Controller
2824
{
29-
#[Endpoint('List teams', 'Return the authenticated user\'s teams as a JSON:API collection.')]
30-
#[ResponseFromApiResource(TeamResource::class, Team::class, collection: true)]
25+
#[Endpoint(title: 'List teams', description: 'Return the authenticated user\'s teams as a JSON:API collection.')]
3126
public function index(Request $request): JsonResponse
3227
{
3328
return TeamResource::collection($request->user()->teams()->get())
3429
->response($request);
3530
}
3631

37-
#[Endpoint('Create a team', 'Create a new team owned by the authenticated user.')]
38-
#[ResponseFromApiResource(TeamResource::class, Team::class, status: Response::HTTP_CREATED)]
32+
#[Endpoint(title: 'Create a team', description: 'Create a new team owned by the authenticated user.')]
3933
public function store(SaveTeamRequest $request, CreateTeam $createTeam): JsonResponse
4034
{
4135
$team = $createTeam->handle($request->user(), $request->validated('name'));
4236

4337
return (new TeamResource($team))
38+
->ignoreFieldsAndIncludesInQueryString()
4439
->response($request)
4540
->setStatusCode(Response::HTTP_CREATED);
4641
}
4742

48-
#[Endpoint('Show a team', 'Return the team resource with its members, pending invitations, caller permissions, and assignable roles under meta.')]
49-
#[ResponseFromApiResource(TeamResource::class, Team::class, additional: [
50-
'meta' => [
51-
'members' => [
52-
[
53-
'id' => '1',
54-
'type' => 'members',
55-
'attributes' => [
56-
'name' => 'Jane Doe',
57-
'email' => 'jane@example.com',
58-
'role' => 'owner',
59-
'role_label' => 'Owner',
60-
],
61-
],
62-
],
63-
'invitations' => [
64-
[
65-
'id' => '1',
66-
'type' => 'team_invitations',
67-
'attributes' => [
68-
'code' => 'abc123',
69-
'email' => 'bob@example.com',
70-
'role' => 'member',
71-
'role_label' => 'Member',
72-
'expires_at' => '2026-04-20T10:00:00+00:00',
73-
'created_at' => '2026-04-17T10:00:00+00:00',
74-
],
75-
],
76-
],
77-
'permissions' => [
78-
'canUpdateTeam' => true,
79-
'canDeleteTeam' => true,
80-
'canAddMember' => true,
81-
'canUpdateMember' => true,
82-
'canRemoveMember' => true,
83-
'canCreateInvitation' => true,
84-
'canCancelInvitation' => true,
85-
],
86-
'available_roles' => [
87-
['value' => 'admin', 'label' => 'Admin'],
88-
['value' => 'member', 'label' => 'Member'],
89-
],
90-
],
91-
])]
43+
#[Endpoint(title: 'Show a team', description: 'Return the team resource with its members, pending invitations, caller permissions, and assignable roles under meta.')]
9244
public function show(Request $request, Team $team): JsonResponse
9345
{
9446
$user = $request->user();
9547

9648
return (new TeamResource($team))
49+
->ignoreFieldsAndIncludesInQueryString()
9750
->additional([
9851
'meta' => [
9952
'members' => MemberResource::collection($team->members()->get()),
10053
'invitations' => TeamInvitationResource::collection(
10154
$team->invitations()->whereNull('accepted_at')->get(),
10255
),
103-
'permissions' => $user->toTeamPermissions($team),
56+
'permissions' => $user->toTeamPermissions($team)->toArray(),
10457
'available_roles' => TeamRole::assignable(),
10558
],
10659
])
10760
->response($request);
10861
}
10962

110-
#[Endpoint('Update a team', 'Update the team name. Only owners may update a team.')]
111-
#[ResponseFromApiResource(TeamResource::class, Team::class)]
63+
#[Endpoint(title: 'Update a team', description: 'Update the team name. Only owners may update a team.')]
11264
public function update(SaveTeamRequest $request, Team $team): JsonResponse
11365
{
11466
Gate::authorize('update', $team);
@@ -121,11 +73,12 @@ public function update(SaveTeamRequest $request, Team $team): JsonResponse
12173
return $team;
12274
});
12375

124-
return (new TeamResource($team))->response($request);
76+
return (new TeamResource($team))
77+
->ignoreFieldsAndIncludesInQueryString()
78+
->response($request);
12579
}
12680

127-
#[Endpoint('Delete a team', 'Soft delete a team. Requires the team name as confirmation.')]
128-
#[ScribeResponse(status: Response::HTTP_NO_CONTENT, description: 'No Content')]
81+
#[Endpoint(title: 'Delete a team', description: 'Soft delete a team. Requires the team name as confirmation.')]
12982
public function destroy(DeleteTeamRequest $request, Team $team): Response
13083
{
13184
DB::transaction(function () use ($team) {

kits/API/Teams/app/Http/Controllers/Teams/TeamInvitationController.php

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,17 @@
99
use App\Models\Team;
1010
use App\Models\TeamInvitation;
1111
use App\Notifications\Teams\TeamInvitation as TeamInvitationNotification;
12+
use Dedoc\Scramble\Attributes\Endpoint;
13+
use Dedoc\Scramble\Attributes\Group;
1214
use Illuminate\Http\JsonResponse;
1315
use Illuminate\Http\Response;
1416
use Illuminate\Support\Facades\Gate;
1517
use Illuminate\Support\Facades\Notification;
16-
use Knuckles\Scribe\Attributes\Authenticated;
17-
use Knuckles\Scribe\Attributes\BodyParam;
18-
use Knuckles\Scribe\Attributes\Endpoint;
19-
use Knuckles\Scribe\Attributes\Group;
20-
use Knuckles\Scribe\Attributes\Response as ScribeResponse;
21-
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
2218

2319
#[Group('Team Invitations')]
24-
#[Authenticated]
2520
class TeamInvitationController extends Controller
2621
{
27-
#[Endpoint('Invite a user', 'Send an invitation email to join the team. Owners and admins may invite members.')]
28-
#[BodyParam('email', 'string', required: true, description: 'The email address of the user to invite.', example: 'jane@example.com')]
29-
#[BodyParam('role', 'string', required: true, description: 'The role the invited user will have on the team.', example: TeamRole::Member->value)]
30-
#[ResponseFromApiResource(TeamInvitationResource::class, TeamInvitation::class, status: Response::HTTP_CREATED)]
22+
#[Endpoint(title: 'Invite a user', description: 'Send an invitation email to join the team. Owners and admins may invite members.')]
3123
public function store(CreateTeamInvitationRequest $request, Team $team): JsonResponse
3224
{
3325
Gate::authorize('inviteMember', $team);
@@ -43,12 +35,12 @@ public function store(CreateTeamInvitationRequest $request, Team $team): JsonRes
4335
->notify(new TeamInvitationNotification($invitation));
4436

4537
return (new TeamInvitationResource($invitation))
38+
->ignoreFieldsAndIncludesInQueryString()
4639
->response($request)
4740
->setStatusCode(Response::HTTP_CREATED);
4841
}
4942

50-
#[Endpoint('Cancel an invitation', 'Cancel a pending invitation for the team.')]
51-
#[ScribeResponse(status: Response::HTTP_NO_CONTENT, description: 'No Content')]
43+
#[Endpoint(title: 'Cancel an invitation', description: 'Cancel a pending invitation for the team.')]
5244
public function destroy(Team $team, TeamInvitation $invitation): Response
5345
{
5446
abort_unless($invitation->team_id === $team->id, Response::HTTP_NOT_FOUND);

kits/API/Teams/app/Http/Controllers/Teams/TeamMemberController.php

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,16 @@
88
use App\Http\Resources\MemberResource;
99
use App\Models\Team;
1010
use App\Models\User;
11+
use Dedoc\Scramble\Attributes\Endpoint;
12+
use Dedoc\Scramble\Attributes\Group;
1113
use Illuminate\Http\JsonResponse;
1214
use Illuminate\Http\Response;
1315
use Illuminate\Support\Facades\Gate;
14-
use Knuckles\Scribe\Attributes\Authenticated;
15-
use Knuckles\Scribe\Attributes\Endpoint;
16-
use Knuckles\Scribe\Attributes\Group;
17-
use Knuckles\Scribe\Attributes\Response as ScribeResponse;
18-
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
1916

2017
#[Group('Team Members')]
21-
#[Authenticated]
2218
class TeamMemberController extends Controller
2319
{
24-
#[Endpoint('Update a member role', 'Update the role of a member on the team. Only team owners may update member roles.')]
25-
#[ResponseFromApiResource(MemberResource::class, User::class)]
20+
#[Endpoint(title: 'Update a member role', description: 'Update the role of a member on the team. Only team owners may update member roles.')]
2621
public function update(UpdateTeamMemberRequest $request, Team $team, User $user): JsonResponse
2722
{
2823
Gate::authorize('updateMember', $team);
@@ -38,12 +33,12 @@ public function update(UpdateTeamMemberRequest $request, Team $team, User $user)
3833

3934
$member = $team->members()->where('users.id', $user->id)->firstOrFail();
4035

41-
return (new MemberResource($member))->response($request);
36+
return (new MemberResource($member))
37+
->ignoreFieldsAndIncludesInQueryString()
38+
->response($request);
4239
}
4340

44-
#[Endpoint('Remove a member', 'Remove a member from the team. The team owner cannot be removed.')]
45-
#[ScribeResponse(status: Response::HTTP_NO_CONTENT, description: 'No Content')]
46-
#[ScribeResponse(status: Response::HTTP_NOT_FOUND, description: 'The user is not a member of the team.')]
41+
#[Endpoint(title: 'Remove a member', description: 'Remove a member from the team. The team owner cannot be removed.')]
4742
public function destroy(Team $team, User $user): Response
4843
{
4944
Gate::authorize('removeMember', $team);

kits/API/Teams/app/Http/Resources/TeamResource.php

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

77
class TeamResource extends JsonApiResource
88
{
9+
protected bool $usesRequestQueryString = false;
10+
911
/**
1012
* The resource's attributes.
1113
*/

0 commit comments

Comments
 (0)