diff options
author | Christian Fehmer <[email protected]> | 2024-08-09 12:39:27 +0200 |
---|---|---|
committer | GitHub <[email protected]> | 2024-08-09 12:39:27 +0200 |
commit | c50535cd0f2c801550ffeafca5a1de142ec341b1 (patch) | |
tree | 836ba8c0ab6387123c133cfff563eeacad0ab67a /packages | |
parent | 6c9148624e4014f4093233df063979fb872d40ad (diff) | |
download | monkeytype-c50535cd0f2c801550ffeafca5a1de142ec341b1.tar.gz monkeytype-c50535cd0f2c801550ffeafca5a1de142ec341b1.zip |
impr: use tsrest for public endpoints (@fehmer) (#5716)
!nuf
Diffstat (limited to 'packages')
-rw-r--r-- | packages/contracts/src/index.ts | 2 | ||||
-rw-r--r-- | packages/contracts/src/public.ts | 70 | ||||
-rw-r--r-- | packages/contracts/src/schemas/api.ts | 8 | ||||
-rw-r--r-- | packages/contracts/src/schemas/public.ts | 15 | ||||
-rw-r--r-- | packages/contracts/src/schemas/shared.ts | 14 | ||||
-rw-r--r-- | packages/contracts/src/schemas/util.ts | 16 | ||||
-rw-r--r-- | packages/shared-types/src/index.ts | 11 |
7 files changed, 119 insertions, 17 deletions
diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index e7f383639..1224a0e19 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -4,6 +4,7 @@ import { apeKeysContract } from "./ape-keys"; import { configsContract } from "./configs"; import { presetsContract } from "./presets"; import { psasContract } from "./psas"; +import { publicContract } from "./public"; const c = initContract(); @@ -13,4 +14,5 @@ export const contract = c.router({ configs: configsContract, presets: presetsContract, psas: psasContract, + public: publicContract, }); diff --git a/packages/contracts/src/public.ts b/packages/contracts/src/public.ts new file mode 100644 index 000000000..93b2b81f9 --- /dev/null +++ b/packages/contracts/src/public.ts @@ -0,0 +1,70 @@ +import { initContract } from "@ts-rest/core"; +import { z } from "zod"; +import { + CommonResponses, + EndpointMetadata, + responseWithData, +} from "./schemas/api"; +import { SpeedHistogramSchema, TypingStatsSchema } from "./schemas/public"; +import { Mode2Schema, ModeSchema } from "./schemas/shared"; +import { LanguageSchema } from "./schemas/util"; + +export const GetSpeedHistogramQuerySchema = z + .object({ + language: LanguageSchema, + mode: ModeSchema, + mode2: Mode2Schema, + }) + .strict(); +export type GetSpeedHistogramQuery = z.infer< + typeof GetSpeedHistogramQuerySchema +>; + +export const GetSpeedHistogramResponseSchema = + responseWithData(SpeedHistogramSchema); +export type GetSpeedHistogramResponse = z.infer< + typeof GetSpeedHistogramResponseSchema +>; + +export const GetTypingStatsResponseSchema = responseWithData(TypingStatsSchema); +export type GetTypingStatsResponse = z.infer< + typeof GetTypingStatsResponseSchema +>; + +const c = initContract(); +export const publicContract = c.router( + { + getSpeedHistogram: { + summary: "get speed histogram", + description: + "get number of users personal bests grouped by wpm level (multiples of ten)", + method: "GET", + path: "/speedHistogram", + query: GetSpeedHistogramQuerySchema, + responses: { + 200: GetSpeedHistogramResponseSchema, + }, + }, + + getTypingStats: { + summary: "get typing stats", + description: "get number of tests and time users spend typing.", + method: "GET", + path: "/typingStats", + responses: { + 200: GetTypingStatsResponseSchema, + }, + }, + }, + { + pathPrefix: "/public", + strictStatusCodes: true, + metadata: { + openApiTags: "public", + authenticationOptions: { + isPublic: true, + }, + } as EndpointMetadata, + commonResponses: CommonResponses, + } +); diff --git a/packages/contracts/src/schemas/api.ts b/packages/contracts/src/schemas/api.ts index f90e47dbd..305d6100a 100644 --- a/packages/contracts/src/schemas/api.ts +++ b/packages/contracts/src/schemas/api.ts @@ -1,6 +1,12 @@ import { z, ZodSchema } from "zod"; -export type OpenApiTag = "configs" | "presets" | "ape-keys" | "admin" | "psas"; +export type OpenApiTag = + | "configs" + | "presets" + | "ape-keys" + | "admin" + | "psas" + | "public"; export type EndpointMetadata = { /** Authentication options, by default a bearer token is required. */ diff --git a/packages/contracts/src/schemas/public.ts b/packages/contracts/src/schemas/public.ts new file mode 100644 index 000000000..5a89122eb --- /dev/null +++ b/packages/contracts/src/schemas/public.ts @@ -0,0 +1,15 @@ +import { z } from "zod"; +import { StringNumberSchema } from "./util"; + +export const SpeedHistogramSchema = z.record( + StringNumberSchema, + z.number().int() +); +export type SpeedHistogram = z.infer<typeof SpeedHistogramSchema>; + +export const TypingStatsSchema = z.object({ + timeTyping: z.number().nonnegative(), + testsCompleted: z.number().int().nonnegative(), + testsStarted: z.number().int().nonnegative(), +}); +export type TypingStats = z.infer<typeof TypingStatsSchema>; diff --git a/packages/contracts/src/schemas/shared.ts b/packages/contracts/src/schemas/shared.ts index 0826839a8..5eb97b5d4 100644 --- a/packages/contracts/src/schemas/shared.ts +++ b/packages/contracts/src/schemas/shared.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { literal, z } from "zod"; import { StringNumberSchema } from "./util"; //used by config and shared @@ -33,9 +33,19 @@ export const PersonalBestsSchema = z.object({ }); export type PersonalBests = z.infer<typeof PersonalBestsSchema>; -//used by user and config +//used by user, config, public export const ModeSchema = PersonalBestsSchema.keyof(); export type Mode = z.infer<typeof ModeSchema>; + +export const Mode2Schema = z.union( + [StringNumberSchema, literal("zen"), literal("custom")], + { + errorMap: () => ({ + message: 'Needs to be either a number, "zen" or "custom."', + }), + } +); + export type Mode2<M extends Mode> = M extends M ? keyof PersonalBests[M] : never; diff --git a/packages/contracts/src/schemas/util.ts b/packages/contracts/src/schemas/util.ts index 74e5ed963..25d9a4b77 100644 --- a/packages/contracts/src/schemas/util.ts +++ b/packages/contracts/src/schemas/util.ts @@ -1,8 +1,12 @@ import { z, ZodString } from "zod"; -export const StringNumberSchema = z.custom<`${number}`>((val) => { - return typeof val === "string" ? /^\d+$/.test(val) : false; -}); +export const StringNumberSchema = z + + .custom<`${number}`>((val) => { + if (typeof val === "number") val = val.toString(); + return typeof val === "string" ? /^\d+$/.test(val) : false; + }, 'Needs to be a number or a number represented as a string e.g. "10".') + .transform(String); export type StringNumber = z.infer<typeof StringNumberSchema>; @@ -13,3 +17,9 @@ export type Id = z.infer<typeof IdSchema>; export const TagSchema = token().max(50); export type Tag = z.infer<typeof TagSchema>; + +export const LanguageSchema = z + .string() + .max(50) + .regex(/^[a-zA-Z0-9_+]+$/); +export type Language = z.infer<typeof LanguageSchema>; diff --git a/packages/shared-types/src/index.ts b/packages/shared-types/src/index.ts index fe8d50ea6..d069ecd3c 100644 --- a/packages/shared-types/src/index.ts +++ b/packages/shared-types/src/index.ts @@ -301,17 +301,6 @@ export type ResultFilters = { } & Record<string, boolean>; }; -export type SpeedHistogram = { - [key: string]: number; -}; - -export type PublicTypingStats = { - type: string; - timeTyping: number; - testsCompleted: number; - testsStarted: number; -}; - export type LeaderboardEntry = { _id: string; wpm: number; |