Lines 100.00% 1 / 1
Functions and Methods 100.00% 1 / 1
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
Me 100.00% 1 / 1 100.00% 1 / 1 1 100.00% 1 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
1<?php
2
3declare(strict_types=1);
4
5namespace App\ApiResource;
6
7use ApiPlatform\Metadata\ApiResource;
8use ApiPlatform\Metadata\Get;
9use ApiPlatform\Metadata\Patch;
10use App\Entity\User;
11use App\Enum\ProfilePart;
12use App\State\MeProcessor;
13use App\State\MeProvider;
14use Symfony\Component\Serializer\Attribute\Groups;
15use Symfony\Component\Validator\Constraints as Assert;
16
17/**
18 * GET /api/me: the signed-in user. PATCH /api/me: the user sets the agent name, replaces the
19 * custom medals, chooses the visible profile parts or saves the Bannergress sync, the only
20 * writable fields.
21 */
22#[ApiResource(
23    description: 'The signed-in user. customMedals are the custom medals (anomaly, annual and event medals) as 4e-website stores them: medal keys of its MedalChecker::$customMedals with the tier (anomaly, event; "" for a medal without tiers) or the level 1 (bronze) to 5 (onyx) (annual). visibleParts are the parts of the profile other signed-in users see (agent, avatar, latest_stats, stats_history, custom_medals, bannergress; the others only with agent); visibilityChosen is false until the user chose them; avatar is the Google profile picture as a 100 px JPEG data: URI (null without one) (on /profile or with PATCH), until then everything is private. With PATCH users set their Ingress agent name (the first agent stats uploaded set it when it is not set yet; agent stats are only accepted for this agent, ignoring case; no other user may have it), replace groups of custom medals (a JSON merge patch: groups not sent stay) or choose visibleParts (all of them; without agent none is stored). bannergress is what the app synced from Bannergress (it needs a Bannergress sign-in): the number of banners published (with a mission the agent created), on the to-do and done lists, publishedBanners (all of them), todoBanners (the newest 20) and, written only, doneBanners (the whole done list, read with /api/done-banners); a banner has id, title, missions and picture (relative to https://api.bannergress.com). PATCH replaces it and the server sets syncedAt.',
24    operations: [
25        new Get(uriTemplate: '/me', security: "is_granted('ROLE_USER')", provider: MeProvider::class),
26        new Patch(
27            uriTemplate: '/me',
28            collectDenormalizationErrors: true,
29            security: "is_granted('ROLE_USER')",
30            // /me has no URI variables: without it API Platform would not load the user to patch
31            read: true,
32            provider: MeProvider::class,
33            processor: MeProcessor::class,
34        ),
35    ],
36    normalizationContext: ['groups' => [self::READ]],
37    denormalizationContext: ['groups' => [self::WRITE]],
38)]
39final class Me
40{
41    public const string READ = 'me:read';
42
43    public const string WRITE = 'me:write';
44
45    public function __construct(
46        #[Groups([self::READ])]
47        public readonly int $id,
48        #[Groups([self::READ])]
49        public readonly string $identifier,
50        #[Groups([self::READ])]
51        public readonly string $role,
52        /** Null until the user or the first stats upload sets it; PATCH without it (or null) keeps it. */
53        #[Groups([self::READ, self::WRITE])]
54        #[Assert\NotBlank(allowNull: true, normalizer: 'trim')]
55        #[Assert\Regex(pattern: User::AGENT_NAME_PATTERN, message: User::AGENT_NAME_INVALID, normalizer: 'trim')]
56        public ?string $agentName,
57        /** PATCH without it (or null) keeps them. */
58        #[Groups([self::READ, self::WRITE])]
59        #[Assert\Valid]
60        public ?CustomMedals $customMedals = null,
61        /**
62         * The profile parts other users see; PATCH replaces them (without the agent part none are
63         * stored) and counts as chosen; without it (or null) keeps them.
64         *
65         * @var list<ProfilePart>|null
66         */
67        #[Groups([self::READ, self::WRITE])]
68        public ?array $visibleParts = null,
69        /** False until the user chose which profile parts other users see (the app asks). */
70        #[Groups([self::READ])]
71        public bool $visibilityChosen = false,
72        /** The Google profile picture (100 px JPEG data: URI), null without one. */
73        #[Groups([self::READ])]
74        public ?string $avatar = null,
75        /** What the app synced from Bannergress, null until it did; PATCH replaces it, without it (or null) keeps it. */
76        #[Groups([self::READ, self::WRITE])]
77        #[Assert\Valid]
78        public ?Bannergress $bannergress = null,
79    ) {
80    }
81}