Skip to content

Commit aab46e3

Browse files
Copilotnikajorjika
andcommitted
Implement API endpoints for inbox dashboard with comprehensive test coverage
Co-authored-by: nikajorjika <[email protected]>
1 parent df89a64 commit aab46e3

File tree

9 files changed

+531
-3
lines changed

9 files changed

+531
-3
lines changed

routes/api.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
use Illuminate\Support\Facades\Route;
4+
use Redberry\MailboxForLaravel\Http\Controllers\Api\InboxController;
5+
use Redberry\MailboxForLaravel\Http\Controllers\Api\MessagesController;
6+
use Redberry\MailboxForLaravel\Http\Controllers\AssetController;
7+
use Redberry\MailboxForLaravel\Http\Controllers\SendTestMailController;
8+
9+
if (! config('inbox.enabled', true)) {
10+
return;
11+
}
12+
13+
Route::middleware(array_merge(
14+
config('inbox.middleware', ['web']),
15+
['mailbox.authorize']
16+
))
17+
->prefix(config('inbox.route', 'mailbox') . '/api')
18+
->name('inbox.api.')
19+
->group(function () {
20+
// Messages endpoints
21+
Route::get('/messages', [MessagesController::class, 'index'])
22+
->name('messages.index');
23+
24+
Route::get('/messages/{id}', [MessagesController::class, 'show'])
25+
->name('messages.show')
26+
->where('id', '[A-Za-z0-9_.\\-]+');
27+
28+
Route::post('/messages/{id}/seen', [MessagesController::class, 'markAsSeen'])
29+
->name('messages.seen')
30+
->where('id', '[A-Za-z0-9_.\\-]+');
31+
32+
Route::delete('/messages/{id}', [MessagesController::class, 'destroy'])
33+
->name('messages.destroy')
34+
->where('id', '[A-Za-z0-9_.\\-]+');
35+
36+
// Inbox operations
37+
Route::post('/clear', [InboxController::class, 'clear'])
38+
->name('clear');
39+
40+
Route::get('/stats', [InboxController::class, 'stats'])
41+
->name('stats');
42+
43+
Route::post('/test-email', SendTestMailController::class)
44+
->name('test-email');
45+
46+
// Assets (keeping the same pattern)
47+
Route::get('/messages/{message}/attachments/{asset}', AssetController::class)
48+
->name('asset');
49+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Redberry\MailboxForLaravel\Http\Controllers\Api;
4+
5+
use Illuminate\Http\JsonResponse;
6+
use Illuminate\Http\Request;
7+
use Redberry\MailboxForLaravel\CaptureService;
8+
9+
class InboxController
10+
{
11+
public function __construct(
12+
protected CaptureService $captureService
13+
) {}
14+
15+
/**
16+
* Clear all messages from the inbox.
17+
*/
18+
public function clear(): JsonResponse
19+
{
20+
$this->captureService->clearAll();
21+
22+
return response()->json(['message' => 'Inbox cleared successfully']);
23+
}
24+
25+
/**
26+
* Get inbox statistics.
27+
*/
28+
public function stats(): JsonResponse
29+
{
30+
$messages = $this->captureService->all();
31+
$total = count($messages);
32+
$unread = 0;
33+
34+
foreach ($messages as $message) {
35+
if (! $message['seen_at']) {
36+
$unread++;
37+
}
38+
}
39+
40+
return response()->json([
41+
'total' => $total,
42+
'unread' => $unread,
43+
'read' => $total - $unread,
44+
]);
45+
}
46+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Redberry\MailboxForLaravel\Http\Controllers\Api;
4+
5+
use Illuminate\Http\JsonResponse;
6+
use Illuminate\Http\Request;
7+
use Redberry\MailboxForLaravel\CaptureService;
8+
9+
class MessagesController
10+
{
11+
public function __construct(
12+
protected CaptureService $captureService
13+
) {}
14+
15+
/**
16+
* Get paginated list of messages.
17+
*/
18+
public function index(Request $request): JsonResponse
19+
{
20+
$page = max(1, (int) $request->get('page', 1));
21+
$perPage = min(100, max(1, (int) $request->get('per_page', 50)));
22+
23+
$result = $this->captureService->list($page, $perPage);
24+
25+
return response()->json([
26+
'data' => array_values($result['data'] ?? $result),
27+
'total' => $result['total'] ?? count($result),
28+
'page' => $result['page'] ?? $page,
29+
'per_page' => $result['per_page'] ?? $perPage,
30+
'last_page' => (int) ceil(($result['total'] ?? count($result)) / $perPage),
31+
]);
32+
}
33+
34+
/**
35+
* Get a specific message by ID.
36+
*/
37+
public function show(string $id): JsonResponse
38+
{
39+
$message = $this->captureService->get($id);
40+
41+
if (! $message) {
42+
return response()->json(['message' => 'Message not found'], 404);
43+
}
44+
45+
return response()->json($message);
46+
}
47+
48+
/**
49+
* Mark a message as seen.
50+
*/
51+
public function markAsSeen(string $id): JsonResponse
52+
{
53+
$message = $this->captureService->update($id, ['seen_at' => now()]);
54+
55+
if (! $message) {
56+
return response()->json(['message' => 'Message not found'], 404);
57+
}
58+
59+
return response()->json($message);
60+
}
61+
62+
/**
63+
* Delete a specific message.
64+
*/
65+
public function destroy(string $id): JsonResponse
66+
{
67+
try {
68+
$this->captureService->delete($id);
69+
return response()->json(['message' => 'Message deleted successfully']);
70+
} catch (\InvalidArgumentException $e) {
71+
return response()->json(['message' => 'Invalid message ID'], 400);
72+
}
73+
}
74+
}

src/Http/Controllers/InboxController.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,30 @@
22

33
namespace Redberry\MailboxForLaravel\Http\Controllers;
44

5+
use Illuminate\Http\JsonResponse;
56
use Illuminate\Http\Request;
67
use Illuminate\Support\Facades\View;
78
use Redberry\MailboxForLaravel\CaptureService;
89

910
class InboxController
1011
{
11-
public function __invoke(Request $request, CaptureService $service): \Illuminate\Contracts\View\View
12+
public function __invoke(Request $request, CaptureService $service): \Illuminate\Contracts\View\View|JsonResponse
1213
{
13-
$messages = $service->all(); // adapt to your API
14+
$page = max(1, (int) $request->get('page', 1));
15+
$perPage = $request->wantsJson() ? min(100, max(1, (int) $request->get('per_page', 50))) : PHP_INT_MAX;
16+
17+
$messages = $perPage === PHP_INT_MAX ? $service->all() : $service->list($page, $perPage);
18+
19+
// Return JSON for API requests
20+
if ($request->wantsJson()) {
21+
return response()->json([
22+
'data' => array_values($messages['data'] ?? $messages),
23+
'total' => $messages['total'] ?? count($messages),
24+
'page' => $messages['page'] ?? $page,
25+
'per_page' => $messages['per_page'] ?? $perPage,
26+
'last_page' => (int) ceil(($messages['total'] ?? count($messages)) / $perPage),
27+
]);
28+
}
1429

1530
$payload = [
1631
'messages' => array_values($messages['data'] ?? $messages), // support both shapes

src/InboxServiceProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public function configurePackage(Package $package): void
1818
$package
1919
->name('mailbox-for-laravel')
2020
->hasConfigFile('inbox')
21-
->hasRoutes('inbox')
21+
->hasRoutes(['inbox', 'api'])
2222
->hasViews('inbox')
2323
->hasCommands([
2424
Commands\InstallCommand::class,
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
use Illuminate\Support\Facades\Gate;
4+
use Redberry\MailboxForLaravel\CaptureService;
5+
6+
describe('API Inbox Controller', function () {
7+
beforeEach(function () {
8+
config()->set('inbox.public', true);
9+
config()->set('inbox.enabled', true);
10+
11+
// Clear any existing messages
12+
$captureService = $this->app->make(CaptureService::class);
13+
$captureService->clearAll();
14+
});
15+
16+
it('clears all messages via API', function () {
17+
$captureService = $this->app->make(CaptureService::class);
18+
19+
// Store some test messages
20+
$captureService->store([
21+
'raw' => 'test email 1',
22+
'subject' => 'Test Subject 1',
23+
'timestamp' => time(),
24+
]);
25+
26+
$captureService->store([
27+
'raw' => 'test email 2',
28+
'subject' => 'Test Subject 2',
29+
'timestamp' => time() + 1,
30+
]);
31+
32+
// Verify messages exist
33+
$messages = $captureService->all();
34+
expect(count($messages))->toBe(2);
35+
36+
$response = $this->postJson('/mailbox/api/clear');
37+
38+
$response->assertStatus(200)
39+
->assertJson(['message' => 'Inbox cleared successfully']);
40+
41+
// Verify all messages are cleared
42+
$messagesAfter = $captureService->all();
43+
expect(count($messagesAfter))->toBe(0);
44+
});
45+
46+
it('returns inbox statistics via API', function () {
47+
$captureService = $this->app->make(CaptureService::class);
48+
49+
// Store some test messages - some read, some unread
50+
$messageId1 = $captureService->store([
51+
'raw' => 'test email 1',
52+
'subject' => 'Test Subject 1',
53+
'timestamp' => time(),
54+
]);
55+
56+
$messageId2 = $captureService->store([
57+
'raw' => 'test email 2',
58+
'subject' => 'Test Subject 2',
59+
'timestamp' => time() + 1,
60+
]);
61+
62+
$messageId3 = $captureService->store([
63+
'raw' => 'test email 3',
64+
'subject' => 'Test Subject 3',
65+
'timestamp' => time() + 2,
66+
]);
67+
68+
// Mark one as seen
69+
$captureService->update($messageId1, ['seen_at' => now()]);
70+
71+
$response = $this->getJson('/mailbox/api/stats');
72+
73+
$response->assertStatus(200)
74+
->assertJsonStructure([
75+
'total',
76+
'unread',
77+
'read'
78+
]);
79+
80+
$data = $response->json();
81+
expect($data['total'])->toBe(3)
82+
->and($data['unread'])->toBe(2)
83+
->and($data['read'])->toBe(1);
84+
});
85+
86+
it('returns correct stats when no messages exist', function () {
87+
$response = $this->getJson('/mailbox/api/stats');
88+
89+
$response->assertStatus(200);
90+
$data = $response->json();
91+
92+
expect($data['total'])->toBe(0)
93+
->and($data['unread'])->toBe(0)
94+
->and($data['read'])->toBe(0);
95+
});
96+
});

0 commit comments

Comments
 (0)