From 4841e0520880cccc237a7875eac6c21d98f27963 Mon Sep 17 00:00:00 2001 From: Paolo Brasolin <paolo.brasolin@eurac.edu> Date: Wed, 16 Mar 2022 14:15:10 +0100 Subject: [PATCH] feat: #be sketch cleanly typed API --- backend/src/api.ts | 103 +++++++++++++++++++++++++++++++++++++++++++ backend/src/index.ts | 3 ++ 2 files changed, 106 insertions(+) create mode 100644 backend/src/api.ts diff --git a/backend/src/api.ts b/backend/src/api.ts new file mode 100644 index 0000000..c524fad --- /dev/null +++ b/backend/src/api.ts @@ -0,0 +1,103 @@ +import { FastifyPluginCallback } from "fastify"; +import { FromSchema } from "json-schema-to-ts"; + +import { connection } from "./db"; + +// NOTE: see https://www.npmjs.com/package/fastify-plugin for TS plugin definition + +const ParamsSchema = { + type: "object", + properties: { + id: { + type: "string", + format: "uuid", + }, + }, + required: ["id"], +} as const; + +const GameSchema = { + type: "object", + properties: { + id: { + type: "string", + format: "uuid", + }, + }, + required: ["id"], +} as const; + +const WordSchema = { + type: "object", + properties: { + id: { type: "string", format: "uuid" }, + image: { type: "string" }, + ocr_confidence: { type: "number", minimum: 0, maximum: 1 }, + ocr_transcript: { type: "string" }, + }, + required: ["id"], +} as const; + +const apiPlugin: FastifyPluginCallback = (fastify, options, next) => { + fastify.route<{ + Reply: FromSchema<typeof WordSchema>; + }>({ + method: "GET", + url: "/word", + schema: { + response: { + 200: WordSchema, + 404: {}, // TODO: JSend error + }, + }, + handler: async (request, reply) => { + // TODO: scope this to game to avoid collisions + const word = await connection<FromSchema<typeof WordSchema>>("words") + .whereBetween("ocr_confidence", [0.4, 0.8]) // i.e. needs improvement but it's not trash + .whereRaw(`"ocr_transcript" ~ '^[[:alpha:]]+$'`) // i.e. no numbers nor symbols + .orderByRaw("RANDOM()") + .first(); + if (word === undefined) { + reply.code(404); + } else { + reply.send({ + id: word.id, + image: word.image, + ocr_confidence: word.ocr_confidence, + ocr_transcript: word.ocr_transcript, + }); + } + }, + }); + + fastify.route<{ + Params: FromSchema<typeof ParamsSchema>; + Reply: FromSchema<typeof GameSchema>; + }>({ + method: "GET", + url: "/games/:id", + schema: { + params: ParamsSchema, + response: { + 200: GameSchema, + 404: {}, // TODO: JSend error + }, + }, + handler: async (request, reply) => { + const game = await connection<FromSchema<typeof GameSchema>>("games") + .where("id", request.params.id) + .first(); + if (game === undefined) { + reply.code(404); + } else { + reply.send({ + id: game.id, + }); + } + }, + }); + + next(); +}; + +export default apiPlugin; diff --git a/backend/src/index.ts b/backend/src/index.ts index 4a91473..e0134ae 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -48,6 +48,9 @@ server.get("/", function (request, reply) { reply.code(200).send("Hello, World!"); }); +import apiRoutes from "./api"; +server.register(apiRoutes, { prefix: "api" }); + server.route({ method: "POST", url: "/GetImage", -- GitLab