Lines
100.00%
26 / 26
Functions and Methods
100.00%
4 / 4
Classes and Traits
100.00%
1 / 1
| Name | Lines | Functions and Methods | CRAP | Classes and Traits | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Bannergress | 100.00% | 26 / 26 | 100.00% | 4 / 4 | 5 | 100.00% | 1 / 1 | |||
| __construct | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | |||||
| fromStored | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 2 | |||||
| fromArray | 100.00% | 8 / 8 | 100.00% | 1 / 1 | 1 | |||||
| toArray | 100.00% | 16 / 16 | 100.00% | 1 / 1 | 1 | |||||
| 1 | <?php | |
| 2 | ||
| 3 | declare(strict_types=1); | |
| 4 | ||
| 5 | namespace App\ApiResource; | |
| 6 | ||
| 7 | use Symfony\Component\Serializer\Attribute\Groups; | |
| 8 | use Symfony\Component\Validator\Constraints as Assert; | |
| 9 | ||
| 10 | /** | |
| 11 | * What the app synced from Bannergress for the signed-in agent: part of Me (GET / PATCH /api/me), | |
| 12 | * and of Agent for the agents who show it. Bannergress only answers signed-in users, so the app | |
| 13 | * signs in there and sends the counts with every published banner, the newest banners of the | |
| 14 | * to-do list and every banner of the done list (stored as DoneBanner, where the user adds the day | |
| 15 | * it was done). | |
| 16 | */ | |
| 17 | final class Bannergress | |
| 18 | { | |
| 19 | /** The newest banners kept of the to-do list. */ | |
| 20 | public const int MAX_BANNERS = 20; | |
| 21 | ||
| 22 | /** The most published banners a sync takes. */ | |
| 23 | public const int MAX_PUBLISHED_BANNERS = 2000; | |
| 24 | ||
| 25 | /** The most banners of the done list a sync takes. */ | |
| 26 | public const int MAX_DONE_BANNERS = 20000; | |
| 27 | ||
| 28 | /** A banner picture as Bannergress has it, relative to PICTURE_BASE. */ | |
| 29 | public const string PICTURE_PATTERN = '/^\/bnrs\/pictures\/[A-Za-z0-9]{1,100}$/'; | |
| 30 | ||
| 31 | public const string PICTURE_BASE = 'https://api.bannergress.com'; | |
| 32 | ||
| 33 | public const string BANNER_BASE = 'https://bannergress.com/banner/'; | |
| 34 | ||
| 35 | /** | |
| 36 | * @param list<array{id: string, title: string, missions: int, picture: string|null, startLatitude?: float|int|null, startLongitude?: float|int|null}> $todoBanners newest first | |
| 37 | * @param list<array{id: string, title: string, missions: int, picture: string|null, startLatitude?: float|int|null, startLongitude?: float|int|null, eventStartDate?: string|null}> $doneBanners newest first, written only | |
| 38 | * @param list<array{id: string, title: string, missions: int, picture: string|null, startLatitude?: float|int|null, startLongitude?: float|int|null}> $publishedBanners newest first | |
| 39 | */ | |
| 40 | public function __construct( | |
| 41 | /** Banners with a mission the agent created. */ | |
| 42 | #[Groups([Me::READ, Me::WRITE, Agent::ITEM])] | |
| 43 | #[Assert\PositiveOrZero] | |
| 44 | public int $published = 0, | |
| 45 | #[Groups([Me::READ, Me::WRITE, Agent::ITEM])] | |
| 46 | #[Assert\PositiveOrZero] | |
| 47 | public int $todo = 0, | |
| 48 | #[Groups([Me::READ, Me::WRITE, Agent::ITEM])] | |
| 49 | #[Assert\PositiveOrZero] | |
| 50 | public int $done = 0, | |
| 51 | #[Groups([Me::READ, Me::WRITE, Agent::ITEM])] | |
| 52 | #[Assert\Count(max: self::MAX_BANNERS)] | |
| 53 | #[Assert\All([new Assert\Collection(fields: [ | |
| 54 | 'id' => [new Assert\Type('string'), new Assert\NotBlank(), new Assert\Length(max: 200)], | |
| 55 | 'title' => [new Assert\Type('string'), new Assert\Length(max: 200)], | |
| 56 | 'missions' => [new Assert\Type('int'), new Assert\PositiveOrZero()], | |
| 57 | 'picture' => new Assert\Optional([new Assert\Type('string'), new Assert\Regex(self::PICTURE_PATTERN)]), | |
| 58 | 'startLatitude' => new Assert\Optional([new Assert\Type(['int', 'float']), new Assert\Range(min: -90, max: 90)]), | |
| 59 | 'startLongitude' => new Assert\Optional([new Assert\Type(['int', 'float']), new Assert\Range(min: -180, max: 180)]), | |
| 60 | ])])] | |
| 61 | public array $todoBanners = [], | |
| 62 | /** Every banner of the done list, newest first: the app sends them, they are stored as DoneBanner. */ | |
| 63 | #[Groups([Me::WRITE])] | |
| 64 | #[Assert\Count(max: self::MAX_DONE_BANNERS)] | |
| 65 | #[Assert\All([new Assert\Collection(fields: [ | |
| 66 | 'id' => [new Assert\Type('string'), new Assert\NotBlank(), new Assert\Length(max: 200)], | |
| 67 | 'title' => [new Assert\Type('string'), new Assert\Length(max: 200)], | |
| 68 | 'missions' => [new Assert\Type('int'), new Assert\PositiveOrZero()], | |
| 69 | 'picture' => new Assert\Optional([new Assert\Type('string'), new Assert\Regex(self::PICTURE_PATTERN)]), | |
| 70 | 'startLatitude' => new Assert\Optional([new Assert\Type(['int', 'float']), new Assert\Range(min: -90, max: 90)]), | |
| 71 | 'startLongitude' => new Assert\Optional([new Assert\Type(['int', 'float']), new Assert\Range(min: -180, max: 180)]), | |
| 72 | // The first day of an event banner: the day it was done unless the user set one | |
| 73 | 'eventStartDate' => new Assert\Optional([new Assert\Type('string'), new Assert\Date()]), | |
| 74 | ])])] | |
| 75 | public array $doneBanners = [], | |
| 76 | /** Every banner with a mission the agent created, newest first. */ | |
| 77 | #[Groups([Me::READ, Me::WRITE, Agent::ITEM])] | |
| 78 | #[Assert\Count(max: self::MAX_PUBLISHED_BANNERS)] | |
| 79 | #[Assert\All([new Assert\Collection(fields: [ | |
| 80 | 'id' => [new Assert\Type('string'), new Assert\NotBlank(), new Assert\Length(max: 200)], | |
| 81 | 'title' => [new Assert\Type('string'), new Assert\Length(max: 200)], | |
| 82 | 'missions' => [new Assert\Type('int'), new Assert\PositiveOrZero()], | |
| 83 | 'picture' => new Assert\Optional([new Assert\Type('string'), new Assert\Regex(self::PICTURE_PATTERN)]), | |
| 84 | 'startLatitude' => new Assert\Optional([new Assert\Type(['int', 'float']), new Assert\Range(min: -90, max: 90)]), | |
| 85 | 'startLongitude' => new Assert\Optional([new Assert\Type(['int', 'float']), new Assert\Range(min: -180, max: 180)]), | |
| 86 | ])])] | |
| 87 | public array $publishedBanners = [], | |
| 88 | /** When the app synced it (ISO 8601); the server sets it. */ | |
| 89 | #[Groups([Me::READ, Agent::ITEM])] | |
| 90 | public ?string $syncedAt = null, | |
| 91 | ) { | |
| 92 | } | |
| 93 | ||
| 94 | /** | |
| 95 | * @param array<string, mixed>|null $stored as stored on the User, null when not synced yet | |
| 96 | */ | |
| 97 | public static function fromStored(?array $stored): ?self | |
| 98 | { | |
| 99 | return null === $stored ? null : self::fromArray($stored); | |
| 100 | } | |
| 101 | ||
| 102 | /** | |
| 103 | * @param array<string, mixed> $stored as stored on the User | |
| 104 | */ | |
| 105 | public static function fromArray(array $stored): self | |
| 106 | { | |
| 107 | /* @var array{published?: int, todo?: int, done?: int, todoBanners?: list<array{id: string, title: string, missions: int, picture: string|null, startLatitude?: float|int|null, startLongitude?: float|int|null}>, doneBanners?: list<array{id: string, title: string, missions: int, picture: string|null, startLatitude?: float|int|null, startLongitude?: float|int|null}>, syncedAt?: string} $stored */ | |
| 108 | return new self( | |
| 109 | $stored['published'] ?? 0, | |
| 110 | $stored['todo'] ?? 0, | |
| 111 | $stored['done'] ?? 0, | |
| 112 | $stored['todoBanners'] ?? [], | |
| 113 | publishedBanners: $stored['publishedBanners'] ?? [], | |
| 114 | syncedAt: $stored['syncedAt'] ?? null, | |
| 115 | ); | |
| 116 | } | |
| 117 | ||
| 118 | /** | |
| 119 | * @return array<string, mixed> for the User, without the done banners (DoneBanner) | |
| 120 | */ | |
| 121 | public function toArray(): array | |
| 122 | { | |
| 123 | $banner = static fn (array $banner): array => [ | |
| 124 | 'id' => $banner['id'], | |
| 125 | 'title' => $banner['title'], | |
| 126 | 'missions' => $banner['missions'], | |
| 127 | 'picture' => $banner['picture'] ?? null, | |
| 128 | 'startLatitude' => $banner['startLatitude'] ?? null, | |
| 129 | 'startLongitude' => $banner['startLongitude'] ?? null, | |
| 130 | ]; | |
| 131 | ||
| 132 | return [ | |
| 133 | 'published' => $this->published, | |
| 134 | 'todo' => $this->todo, | |
| 135 | 'done' => $this->done, | |
| 136 | 'todoBanners' => \array_map($banner, $this->todoBanners), | |
| 137 | 'publishedBanners' => \array_map($banner, $this->publishedBanners), | |
| 138 | 'syncedAt' => $this->syncedAt, | |
| 139 | ]; | |
| 140 | } | |
| 141 | } |