diff --git a/web/lib/models/bookmarks.js b/web/lib/models/bookmarks.js new file mode 100644 index 0000000..51d0110 --- /dev/null +++ b/web/lib/models/bookmarks.js @@ -0,0 +1,22 @@ +import Ajv from "ajv"; +import addFormats from "ajv-formats"; + +const ajv = new Ajv(); +addFormats(ajv); + +const validateId = ajv.compile({ + type: "string", + minLength: 1, + maxLength: 10, +}); + +const validatePostBody = ajv.compile({ + type: "object", + properties: { + title: { type: "string" }, + url: { type: "string", format: "uri" }, + }, + required: ["title", "url"], +}); + +export { validateId, validatePostBody }; diff --git a/web/lib/models/index.js b/web/lib/models/index.js new file mode 100644 index 0000000..ad2a2ab --- /dev/null +++ b/web/lib/models/index.js @@ -0,0 +1,3 @@ +export default { + bookmarks: require("./bookmarks"), +}; diff --git a/web/pages/api/v1/bookmarks/[bookmarkId].js b/web/pages/api/v1/[entityName]/[entityId].js similarity index 55% rename from web/pages/api/v1/bookmarks/[bookmarkId].js rename to web/pages/api/v1/[entityName]/[entityId].js index 00f0603..adf6ed4 100644 --- a/web/pages/api/v1/bookmarks/[bookmarkId].js +++ b/web/pages/api/v1/[entityName]/[entityId].js @@ -1,5 +1,6 @@ import db from "../../../../lib/db"; import { withErrorHandling, withSession } from "../../../../lib/apiHelpers"; +import models from "../../../../lib/models"; const handler = withErrorHandling( withSession(async (req, res, session) => { @@ -8,19 +9,27 @@ const handler = withErrorHandling( switch (method) { case "GET": - await getBookmark(req, res, user); + await getEntity(req, res, user); break; default: - res.setHeader("Allow", ["GET", "POST"]); + res.setHeader("Allow", ["GET"]); res.status(405).end(`Method ${method} Not Allowed`); } }) ); -const getBookmark = async (req, res, user) => { - const { bookmarkId } = req.query; +const getEntity = async (req, res, user) => { + const { entityName, entityId } = req.query; - if (!bookmarkId) { + if (!models[entityName]) { + res.status(404).end(); + return; + } + + var model = models[entityName]; + + if (!model.validateId(entityId)) { + console.warn(model.validateId.errors); res.status(404).end(); return; } @@ -30,14 +39,14 @@ const getBookmark = async (req, res, user) => { KeyConditionExpression: "hk = :hk AND sk = :sk", ExpressionAttributeValues: { ":hk": `user#${user.sub}`, - ":sk": `bookmark#${bookmarkId}`, + ":sk": `${entityName}#${entityId}`, }, }; - let bookmarks = await db.query(params).promise(); + let entities = await db.query(params).promise(); - if (bookmarks.Items.length == 1) { - res.status(200).json(bookmarks.Items[0]); + if (entities.Items.length == 1) { + res.status(200).json(entities.Items[0]); } else { res.status(404).end(); } diff --git a/web/pages/api/v1/[entityName]/index.js b/web/pages/api/v1/[entityName]/index.js new file mode 100644 index 0000000..19a0068 --- /dev/null +++ b/web/pages/api/v1/[entityName]/index.js @@ -0,0 +1,75 @@ +import db from "../../../../lib/db"; +import { withErrorHandling, withSession } from "../../../../lib/apiHelpers"; +import models from "../../../../lib/models"; + +const handler = withErrorHandling( + withSession(async (req, res, session) => { + const { method } = req; + const { user } = session; + + switch (method) { + case "GET": + await getEntities(req, res, user); + break; + case "POST": + await createEntity(req, res, user); + break; + default: + res.setHeader("Allow", ["GET", "POST"]); + res.status(405).end(`Method ${method} Not Allowed`); + } + }) +); + +const getEntities = async (req, res, user) => { + const { entityName } = req.query; + + if (!models[entityName]) { + res.status(404).end(); + return; + } + + const params = { + TableName: process.env.FMN_DYNAMODB_TABLENAME, + KeyConditionExpression: "hk = :hk AND begins_with(sk, :sk)", + ExpressionAttributeValues: { + ":hk": `user#${user.sub}`, + ":sk": `${entityName}#`, + }, + }; + + let entities = await db.query(params).promise(); + res.status(200).json(entities.Items); +}; + +const createEntity = async (req, res, user) => { + const { entityName } = req.query; + + if (!models[entityName]) { + res.status(404).end(); + return; + } + + var model = models[entityName]; + + if (!model.validatePostBody(req.body)) { + console.warn(validatePostBody.errors); + res.status(400).json(validatePostBody.errors); + return; + } + + const params = { + TableName: process.env.FMN_DYNAMODB_TABLENAME, + Item: { + hk: `user#${user.sub}`, + sk: `${entityName}#${"abc"}`, + title: req.body.title, + url: req.body.url, + }, + }; + + await db.put(params).promise(); + res.status(200).json(params.Item); +}; + +export default handler; diff --git a/web/pages/api/v1/bookmarks.js b/web/pages/api/v1/bookmarks.js deleted file mode 100644 index e2c0c48..0000000 --- a/web/pages/api/v1/bookmarks.js +++ /dev/null @@ -1,75 +0,0 @@ -import Ajv from "ajv"; -import addFormats from "ajv-formats"; - -import db from "../../../lib/db"; -import { withErrorHandling, withSession } from "../../../lib/apiHelpers"; - -const handler = withErrorHandling( - withSession(async (req, res, session) => { - const { method } = req; - const { user } = session; - - switch (method) { - case "GET": - await getBookmarks(req, res, user); - break; - case "POST": - await createBookmark(req, res, user); - break; - default: - res.setHeader("Allow", ["GET", "POST"]); - res.status(405).end(`Method ${method} Not Allowed`); - } - }) -); - -const ajv = new Ajv(); -addFormats(ajv); - -const getBookmarks = async (req, res, user) => { - const params = { - TableName: process.env.FMN_DYNAMODB_TABLENAME, - KeyConditionExpression: "hk = :hk AND begins_with(sk, :sk)", - ExpressionAttributeValues: { - ":hk": `user#${user.sub}`, - ":sk": "bookmark#", - }, - }; - - let bookmarks = await db.query(params).promise(); - res.status(200).json(bookmarks.Items); -}; - -const createBookmarkSchema = { - type: "object", - properties: { - title: { type: "string" }, - url: { type: "string", format: "uri" }, - }, - required: ["title", "url"], -}; - -const validateCreateBookmarkSchema = ajv.compile(createBookmarkSchema); - -const createBookmark = async (req, res, user) => { - if (!validateCreateBookmarkSchema(req.body)) { - console.warn(validateCreateBookmarkSchema.errors); - res.status(400).json(validateCreateBookmarkSchema.errors); - return; - } - - const params = { - TableName: process.env.FMN_DYNAMODB_TABLENAME, - Item: { - hk: `user#${user.sub}`, - sk: `bookmark#${"abc"}`, - title: req.body.title, - url: req.body.url, - }, - }; - - await db.put(params).promise(); - res.status(200).json(params.Item); -}; - -export default handler;