Skip to content

Commit a61af30

Browse files
Use DTO's for cached contest and API info data in external contest service.
1 parent 6986069 commit a61af30

File tree

2 files changed

+46
-31
lines changed

2 files changed

+46
-31
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace App\DataTransferObject\Shadowing;
4+
5+
class ContestData
6+
{
7+
public function __construct(
8+
public readonly string $id,
9+
public readonly ?string $externalId,
10+
public readonly string $name,
11+
public readonly ?string $formalName,
12+
public readonly ?string $shortname,
13+
public readonly string $duration,
14+
public readonly ?string $scoreboardFreezeDuration,
15+
public readonly ?string $scoreboardThawTime,
16+
public readonly ?string $scoreboardType,
17+
public readonly int $penaltyTime,
18+
public readonly ?string $startTime,
19+
public readonly ?string $endTime,
20+
public readonly ?bool $allowSubmit,
21+
public readonly ?string $warningMessage,
22+
) {}
23+
}

webapp/src/Service/ExternalContestSourceService.php

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace App\Service;
44

5+
use App\DataTransferObject\ApiInfo;
6+
use App\DataTransferObject\Shadowing\ContestData;
57
use App\Entity\BaseApiEntity;
68
use App\Entity\Clarification;
79
use App\Entity\Contest;
@@ -24,7 +26,7 @@
2426
use Doctrine\DBAL\Exception as DBALException;
2527
use Doctrine\ORM\EntityManagerInterface;
2628
use Doctrine\ORM\NonUniqueResultException;
27-
use JsonException;
29+
use Exception;
2830
use LogicException;
2931
use Psr\Log\LoggerInterface;
3032
use Symfony\Component\DependencyInjection\Attribute\Autowire;
@@ -33,6 +35,7 @@
3335
use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
3436
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
3537
use Symfony\Component\PropertyAccess\PropertyAccess;
38+
use Symfony\Component\Serializer\SerializerInterface;
3639
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
3740
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
3841
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
@@ -46,19 +49,8 @@ class ExternalContestSourceService
4649
protected ?ExternalContestSource $source = null;
4750

4851
protected bool $contestLoaded = false;
49-
/**
50-
* @var array{scoreboard_type: string, external_id: string, end_time: string, duration: string, name: string,
51-
* scoreboard_freeze_duration: string, id: string, allow_submit: bool, allow_submit: bool,
52-
* penalty_time: int, start_time?: string|null, formal_name?: string|null, shortname?: string|null,
53-
* scoreboard_thaw_time?: string, warning_message?: string|null} $cachedContestData
54-
*/
55-
protected ?array $cachedContestData = null;
56-
/**
57-
* @var array{version: string, version_url: string, name: string,
58-
* provider?: array{name?: string, build_date?: string, version?: string},
59-
* domjudge?: array{apiversion: int, version: string, environment: string, doc_url: string}} $cachedApiInfoData
60-
*/
61-
protected ?array $cachedApiInfoData = null;
52+
protected ?ContestData $cachedContestData = null;
53+
protected ?ApiInfo $cachedApiInfoData = null;
6254
protected ?string $loadingError = null;
6355
protected bool $shouldStopReading = false;
6456
/** @var array<string, string> $verdicts */
@@ -109,6 +101,7 @@ public function __construct(
109101
protected readonly EventLogService $eventLog,
110102
protected readonly SubmissionService $submissionService,
111103
protected readonly ScoreboardService $scoreboardService,
104+
protected readonly SerializerInterface $serializer,
112105
#[Autowire('%domjudge.version%')]
113106
string $domjudgeVersion
114107
) {
@@ -159,7 +152,7 @@ public function getContestId(): string
159152
throw new LogicException('The contest source is not valid');
160153
}
161154

162-
return $this->cachedContestData['id'];
155+
return $this->cachedContestData->id;
163156
}
164157

165158
public function getContestName(): string
@@ -168,16 +161,16 @@ public function getContestName(): string
168161
throw new LogicException('The contest source is not valid');
169162
}
170163

171-
return $this->cachedContestData['name'];
164+
return $this->cachedContestData->name;
172165
}
173166

174167
public function getContestStartTime(): ?float
175168
{
176169
if (!$this->isValidContestSource()) {
177170
throw new LogicException('The contest source is not valid');
178171
}
179-
if (isset($this->cachedContestData['start_time'])) {
180-
return Utils::toEpochFloat($this->cachedContestData['start_time']);
172+
if (isset($this->cachedContestData->startTime)) {
173+
return Utils::toEpochFloat($this->cachedContestData->startTime);
181174
} else {
182175
$this->logger->warning('Contest has no start time, is the contest paused?');
183176
return null;
@@ -190,7 +183,7 @@ public function getContestDuration(): string
190183
throw new LogicException('The contest source is not valid');
191184
}
192185

193-
return $this->cachedContestData['duration'];
186+
return $this->cachedContestData->duration;
194187
}
195188

196189
public function getApiVersion(): ?string
@@ -199,7 +192,7 @@ public function getApiVersion(): ?string
199192
throw new LogicException('The contest source is not valid');
200193
}
201194

202-
return $this->cachedApiInfoData['version'] ?? null;
195+
return $this->cachedApiInfoData->version ?? null;
203196
}
204197

205198
public function getApiVersionUrl(): ?string
@@ -208,7 +201,7 @@ public function getApiVersionUrl(): ?string
208201
throw new LogicException('The contest source is not valid');
209202
}
210203

211-
return $this->cachedApiInfoData['version_url'] ?? null;
204+
return $this->cachedApiInfoData->versionUrl ?? null;
212205
}
213206

214207
public function getApiProviderName(): ?string
@@ -217,7 +210,7 @@ public function getApiProviderName(): ?string
217210
throw new LogicException('The contest source is not valid');
218211
}
219212

220-
return $this->cachedApiInfoData['provider']['name'] ?? $this->cachedApiInfoData['name'] ?? null;
213+
return $this->cachedApiInfoData->provider?->name ?? $this->cachedApiInfoData->name;
221214
}
222215

223216
public function getApiProviderVersion(): ?string
@@ -226,7 +219,7 @@ public function getApiProviderVersion(): ?string
226219
throw new LogicException('The contest source is not valid');
227220
}
228221

229-
return $this->cachedApiInfoData['provider']['version'] ?? null;
222+
return $this->cachedApiInfoData->provider?->version ?? $this->cachedApiInfoData->domjudge?->version;
230223
}
231224

232225
public function getApiProviderBuildDate(): ?string
@@ -235,7 +228,7 @@ public function getApiProviderBuildDate(): ?string
235228
throw new LogicException('The contest source is not valid');
236229
}
237230

238-
return $this->cachedApiInfoData['provider']['build_date'] ?? null;
231+
return $this->cachedApiInfoData->provider?->buildDate;
239232
}
240233

241234
public function getLoadingError(): string
@@ -494,7 +487,6 @@ function (
494487
* - A boolean that can be set to true (pass-by-reference) to stop processing
495488
*
496489
* @param resource $filePointer
497-
* @throws JsonException
498490
*/
499491
protected function readEventsFromFile($filePointer, callable $callback): void
500492
{
@@ -555,10 +547,10 @@ protected function loadContest(): void
555547
}
556548
$this->httpClient = $this->httpClient->withOptions($clientOptions);
557549
$contestResponse = $this->httpClient->request('GET', $this->source->getSource());
558-
$this->cachedContestData = $contestResponse->toArray();
550+
$this->cachedContestData = $this->serializer->deserialize($contestResponse->getContent(), ContestData::class, 'json');
559551

560552
$apiInfoResponse = $this->httpClient->request('GET', '');
561-
$this->cachedApiInfoData = $apiInfoResponse->toArray();
553+
$this->cachedApiInfoData = $this->serializer->deserialize($apiInfoResponse->getContent(), ApiInfo::class, 'json');
562554
}
563555
} catch (HttpExceptionInterface|DecodingExceptionInterface|TransportExceptionInterface $e) {
564556
$this->cachedContestData = null;
@@ -580,15 +572,15 @@ protected function loadContest(): void
580572
$this->loadingError = 'event-feed.ndjson not found in archive';
581573
} else {
582574
try {
583-
$this->cachedContestData = $this->dj->jsonDecode(file_get_contents($contestFile));
584-
} catch (JsonException $e) {
575+
$this->cachedContestData = $this->serializer->deserialize(file_get_contents($contestFile), ContestData::class, 'json');
576+
} catch (Exception $e) {
585577
$this->loadingError = $e->getMessage();
586578
}
587579

588580
if (is_file($apiInfoFile)) {
589581
try {
590-
$this->cachedApiInfoData = $this->dj->jsonDecode(file_get_contents($apiInfoFile));
591-
} catch (JsonException $e) {
582+
$this->cachedApiInfoData = $this->serializer->deserialize(file_get_contents($apiInfoFile), ApiInfo::class, 'json');
583+
} catch (Exception $e) {
592584
$this->loadingError = $e->getMessage();
593585
}
594586
}

0 commit comments

Comments
 (0)