Skip to content

Commit 7cd1e41

Browse files
committed
♻️ Improve cache and types (#1514)
1 parent 2e0f95c commit 7cd1e41

File tree

1 file changed

+63
-28
lines changed

1 file changed

+63
-28
lines changed

src/lib/clients/aizu_online_judge.ts

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,9 @@ type AOJChallengeContestAPI = {
3131
};
3232

3333
/**
34-
* Enum representing the types of challenge contests available.
34+
* Represents the types of challenge contests available.
3535
*/
36-
enum ChallengeContestType {
37-
PCK = 'pck',
38-
JAG = 'jag',
39-
}
36+
type ChallengeContestType = 'PCK' | 'JAG';
4037

4138
/**
4239
* Represents a challenge contest in the AOJ
@@ -81,31 +78,25 @@ type AOJTaskAPI = {
8178
type AOJTaskAPIs = AOJTaskAPI[];
8279

8380
/**
84-
* Enum representing PCK contest rounds
81+
* Represents PCK contest rounds
8582
*/
86-
enum PckRound {
87-
PRELIM = 'prelim',
88-
FINAL = 'final',
89-
}
83+
type PckRound = 'PRELIM' | 'FINAL';
9084

9185
/**
92-
* Enum representing JAG contest rounds
86+
* Represents JAG contest rounds
9387
*/
94-
enum JagRound {
95-
PRELIM = 'prelim',
96-
REGIONAL = 'regional',
97-
}
88+
type JagRound = 'PRELIM' | 'REGIONAL';
9889

9990
/**
10091
* A map that associates each type of challenge contest with its corresponding round type.
10192
*
10293
* @typedef {Object} ChallengeRoundMap
103-
* @property {PckRound} ChallengeContestType.PCK - The round type for PCK contests.
104-
* @property {JagRound} ChallengeContestType.JAG - The round type for JAG contests.
94+
* @property {PckRound} PCK - The round type for PCK contests.
95+
* @property {JagRound} JAG - The round type for JAG contests.
10596
*/
10697
type ChallengeRoundMap = {
107-
[ChallengeContestType.PCK]: PckRound;
108-
[ChallengeContestType.JAG]: JagRound;
98+
PCK: PckRound;
99+
JAG: JagRound;
109100
};
110101

111102
/**
@@ -121,6 +112,17 @@ const PENDING = -1;
121112
const DEFAULT_CACHE_TTL = 60 * 60 * 1000; // 1 hour in milliseconds
122113
const DEFAULT_MAX_CACHE_SIZE = 50;
123114

115+
/**
116+
* Configuration options for caching.
117+
*
118+
* @property {number} [timeToLive] - The duration (in milliseconds) for which a cache entry should remain valid.
119+
* @property {number} [maxSize] - The maximum number of entries that the cache can hold.
120+
*/
121+
interface CacheConfig {
122+
timeToLive?: number;
123+
maxSize?: number;
124+
}
125+
124126
/**
125127
* Represents a cache entry with data and a timestamp.
126128
*
@@ -195,6 +197,10 @@ class Cache<T> {
195197
* @param data - The data to be cached.
196198
*/
197199
set(key: string, data: T): void {
200+
if (!key || typeof key !== 'string' || key.length > 255) {
201+
throw new Error('Invalid cache key');
202+
}
203+
198204
if (this.cache.size >= this.maxSize) {
199205
const oldestKey = this.findOldestEntry();
200206

@@ -279,6 +285,11 @@ class Cache<T> {
279285
}
280286
}
281287

288+
interface ApiClientConfig {
289+
contestCache: CacheConfig;
290+
taskCache: CacheConfig;
291+
}
292+
282293
/**
283294
* AojApiClient is a client for interacting with the Aizu Online Judge (AOJ) API.
284295
* It extends the ContestSiteApiClient and provides methods to fetch contests and tasks
@@ -310,6 +321,30 @@ export class AojApiClient extends ContestSiteApiClient {
310321
*/
311322
private readonly taskCache = new Cache<TasksForImport>();
312323

324+
/**
325+
* Constructs an instance of the Aizu Online Judge client.
326+
*
327+
* @param {ApiClientConfig} [config] - Optional configuration object for the API client.
328+
* @param {Cache<ContestsForImport>} [config.contestCache] - Configuration for the contest cache.
329+
* @param {number} [config.contestCache.timeToLive] - Time to live for contest cache entries.
330+
* @param {number} [config.contestCache.maxSize] - Maximum size of the contest cache.
331+
* @param {Cache<TasksForImport>} [config.taskCache] - Configuration for the task cache.
332+
* @param {number} [config.taskCache.timeToLive] - Time to live for task cache entries.
333+
* @param {number} [config.taskCache.maxSize] - Maximum size of the task cache.
334+
*/
335+
constructor(config?: ApiClientConfig) {
336+
super();
337+
338+
this.contestCache = new Cache<ContestsForImport>(
339+
config?.contestCache?.timeToLive,
340+
config?.contestCache?.maxSize,
341+
);
342+
this.taskCache = new Cache<TasksForImport>(
343+
config?.taskCache?.timeToLive,
344+
config?.taskCache?.maxSize,
345+
);
346+
}
347+
313348
/**
314349
* Disposes of the resources used by the client.
315350
* Clears the contest and task caches to free up memory.
@@ -331,10 +366,10 @@ export class AojApiClient extends ContestSiteApiClient {
331366
try {
332367
const results = await Promise.allSettled([
333368
this.fetchCourseContests(),
334-
this.fetchChallengeContests(ChallengeContestType.PCK, PckRound.PRELIM),
335-
this.fetchChallengeContests(ChallengeContestType.PCK, PckRound.FINAL),
336-
this.fetchChallengeContests(ChallengeContestType.JAG, JagRound.PRELIM),
337-
this.fetchChallengeContests(ChallengeContestType.JAG, JagRound.REGIONAL),
369+
this.fetchChallengeContests('PCK', 'PRELIM'),
370+
this.fetchChallengeContests('PCK', 'FINAL'),
371+
this.fetchChallengeContests('JAG', 'PRELIM'),
372+
this.fetchChallengeContests('JAG', 'REGIONAL'),
338373
]);
339374

340375
const [courses, pckPrelims, pckFinals, jagPrelims, jagRegionals] = results.map((result) => {
@@ -492,7 +527,7 @@ export class AojApiClient extends ContestSiteApiClient {
492527
const validateSegment = (segment: string): boolean => {
493528
return (
494529
segment.length <= MAX_SEGMENT_LENGTH &&
495-
/^[a-zA-Z][a-zA-Z0-9]{0,98}[-_a-zA-Z0-9]*$/.test(segment) &&
530+
/^[a-zA-Z](?:[a-zA-Z0-9]|[-_](?=[a-zA-Z0-9])){0,98}[a-zA-Z0-9]$/.test(segment) &&
496531
!segment.includes('..')
497532
);
498533
};
@@ -550,10 +585,10 @@ export class AojApiClient extends ContestSiteApiClient {
550585
try {
551586
const results = await Promise.allSettled([
552587
this.fetchCourseTasks(),
553-
this.fetchChallengeTasks(ChallengeContestType.PCK, PckRound.PRELIM),
554-
this.fetchChallengeTasks(ChallengeContestType.PCK, PckRound.FINAL),
555-
this.fetchChallengeTasks(ChallengeContestType.JAG, JagRound.PRELIM),
556-
this.fetchChallengeTasks(ChallengeContestType.JAG, JagRound.REGIONAL),
588+
this.fetchChallengeTasks('PCK', 'PRELIM'),
589+
this.fetchChallengeTasks('PCK', 'FINAL'),
590+
this.fetchChallengeTasks('JAG', 'PRELIM'),
591+
this.fetchChallengeTasks('JAG', 'REGIONAL'),
557592
]);
558593

559594
const [courses, pckPrelims, pckFinals, jagPrelims, jagRegionals] = results.map((result) => {

0 commit comments

Comments
 (0)