Skip to content

Commit 6299bc1

Browse files
committed
Add dashboard_statistics
1 parent 8801ad9 commit 6299bc1

3 files changed

Lines changed: 195 additions & 0 deletions

File tree

src/Statistics/Controller/AnalyticsController.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\HttpFoundation\Request;
1919
use Symfony\Component\HttpFoundation\Response;
2020
use Symfony\Component\Routing\Attribute\Route;
21+
use Throwable;
2122

2223
/**
2324
* This controller provides REST API to access analytics data.
@@ -356,4 +357,81 @@ public function getTopLocalParts(Request $request): JsonResponse
356357

357358
return $this->json($normalizedData, Response::HTTP_OK);
358359
}
360+
361+
#[Route('/dashboard', name: 'dashboard_statistics', methods: ['GET'])]
362+
#[OA\Get(
363+
path: '/api/v2/analytics/dashboard',
364+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
365+
'Returns dashboard cards with aggregate analytics metrics.',
366+
summary: 'Gets dashboard analytics statistics.',
367+
tags: ['analytics'],
368+
parameters: [
369+
new OA\Parameter(
370+
name: 'php-auth-pw',
371+
description: 'Session key obtained from login',
372+
in: 'header',
373+
required: true,
374+
schema: new OA\Schema(type: 'string')
375+
)
376+
],
377+
responses: [
378+
new OA\Response(
379+
response: 200,
380+
description: 'Success',
381+
content: new OA\JsonContent(
382+
properties: [
383+
new OA\Property(
384+
property: 'total_subscribers',
385+
properties: [
386+
new OA\Property(property: 'value', type: 'integer', example: 48294),
387+
new OA\Property(property: 'change_vs_last_month', type: 'number', format: 'float', example: 12.5),
388+
],
389+
type: 'object'
390+
),
391+
new OA\Property(
392+
property: 'active_campaigns',
393+
properties: [
394+
new OA\Property(property: 'value', type: 'integer', example: 12),
395+
new OA\Property(property: 'change_vs_last_month', type: 'number', format: 'float', example: 0),
396+
],
397+
type: 'object'
398+
),
399+
new OA\Property(
400+
property: 'open_rate',
401+
properties: [
402+
new OA\Property(property: 'value', type: 'number', format: 'float', example: 12),
403+
new OA\Property(property: 'change_vs_last_month', type: 'number', format: 'float', example: 0),
404+
],
405+
type: 'object'
406+
),
407+
new OA\Property(
408+
property: 'bounce_rate',
409+
properties: [
410+
new OA\Property(property: 'value', type: 'number', format: 'float', example: 12),
411+
new OA\Property(property: 'change_vs_last_month', type: 'number', format: 'float', example: 0),
412+
],
413+
type: 'object'
414+
),
415+
],
416+
type: 'object'
417+
)
418+
),
419+
new OA\Response(
420+
response: 403,
421+
description: 'Unauthorized',
422+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
423+
)
424+
]
425+
)]
426+
public function getDashboardStatistics(Request $request): JsonResponse
427+
{
428+
$authUser = $this->requireAuthentication($request);
429+
if (!$authUser->getPrivileges()->has(PrivilegeFlag::Statistics)) {
430+
throw $this->createAccessDeniedException('You are not allowed to access statistics.');
431+
}
432+
433+
$response = $this->analyticsService->getSummaryStatistics();
434+
435+
return $this->json($response, Response::HTTP_OK);
436+
}
359437
}

tests/Integration/Statistics/Controller/AnalyticsControllerTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,38 @@ public function testGetTopLocalPartsWithInvalidLimitParameter(): void
254254
self::assertArrayHasKey('local_parts', $response);
255255
self::assertIsArray($response['local_parts']);
256256
}
257+
258+
public function testGetDashboardStatisticsWithoutSessionKeyReturnsForbidden(): void
259+
{
260+
self::getClient()->request('GET', '/api/v2/analytics/dashboard');
261+
$this->assertHttpForbidden();
262+
}
263+
264+
public function testGetDashboardStatisticsWithValidSessionReturnsCardsData(): void
265+
{
266+
$this->loadFixtures([
267+
AdministratorFixture::class,
268+
AdministratorTokenFixture::class,
269+
SubscriberFixture::class,
270+
MessageFixture::class,
271+
]);
272+
273+
$this->authenticatedJsonRequest('GET', '/api/v2/analytics/dashboard');
274+
$this->assertHttpOkay();
275+
$response = $this->getDecodedJsonResponseContent();
276+
277+
self::assertIsArray($response);
278+
self::assertArrayHasKey('total_subscribers', $response);
279+
self::assertArrayHasKey('active_campaigns', $response);
280+
self::assertArrayHasKey('open_rate', $response);
281+
self::assertArrayHasKey('bounce_rate', $response);
282+
283+
foreach (['total_subscribers', 'active_campaigns', 'open_rate', 'bounce_rate'] as $metric) {
284+
self::assertIsArray($response[$metric]);
285+
self::assertArrayHasKey('value', $response[$metric]);
286+
self::assertArrayHasKey('change_vs_last_month', $response[$metric]);
287+
self::assertIsNumeric($response[$metric]['value']);
288+
self::assertIsNumeric($response[$metric]['change_vs_last_month']);
289+
}
290+
}
257291
}

tests/Unit/Statistics/Controller/AnalyticsControllerTest.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,4 +442,87 @@ public function testGetTopLocalPartsReturnsJsonResponse(): void
442442
'total' => 1,
443443
], json_decode($response->getContent(), true));
444444
}
445+
446+
public function testGetDashboardStatisticsWithoutStatisticsPrivilegeThrowsException(): void
447+
{
448+
$request = new Request();
449+
450+
$this->authentication
451+
->expects(self::once())
452+
->method('authenticateByApiKey')
453+
->with($request)
454+
->willReturn($this->administrator);
455+
456+
$this->privileges
457+
->expects(self::once())
458+
->method('has')
459+
->with(PrivilegeFlag::Statistics)
460+
->willReturn(false);
461+
462+
$this->expectException(AccessDeniedException::class);
463+
$this->expectExceptionMessage('You are not allowed to access statistics.');
464+
465+
$this->controller->getDashboardStatistics($request);
466+
}
467+
468+
public function testGetDashboardStatisticsReturnsJsonResponse(): void
469+
{
470+
$request = new Request();
471+
472+
$this->authentication
473+
->expects(self::once())
474+
->method('authenticateByApiKey')
475+
->with($request)
476+
->willReturn($this->administrator);
477+
478+
$this->privileges
479+
->expects(self::once())
480+
->method('has')
481+
->with(PrivilegeFlag::Statistics)
482+
->willReturn(true);
483+
484+
$this->analyticsService
485+
->expects(self::once())
486+
->method('getSummaryStatistics')
487+
->willReturn([
488+
'total_subscribers' => [
489+
'value' => 80,
490+
'change_vs_last_month' => 10.5,
491+
],
492+
'active_campaigns' => [
493+
'value' => 12,
494+
'change_vs_last_month' => -4.25,
495+
],
496+
'open_rate' => [
497+
'value' => 40.0,
498+
'change_vs_last_month' => 3.3,
499+
],
500+
'bounce_rate' => [
501+
'value' => 6.67,
502+
'change_vs_last_month' => -1.1,
503+
],
504+
]);
505+
506+
$response = $this->controller->getDashboardStatistics($request);
507+
508+
self::assertEquals(Response::HTTP_OK, $response->getStatusCode());
509+
self::assertEquals([
510+
'total_subscribers' => [
511+
'value' => 80,
512+
'change_vs_last_month' => 10.5,
513+
],
514+
'active_campaigns' => [
515+
'value' => 12,
516+
'change_vs_last_month' => -4.25,
517+
],
518+
'open_rate' => [
519+
'value' => 40.0,
520+
'change_vs_last_month' => 3.3,
521+
],
522+
'bounce_rate' => [
523+
'value' => 6.67,
524+
'change_vs_last_month' => -1.1,
525+
],
526+
], json_decode($response->getContent(), true));
527+
}
445528
}

0 commit comments

Comments
 (0)