Skip to content

Commit

Permalink
add entity creation and deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
TimurRin committed Dec 10, 2024
1 parent bfba13b commit 5ac6f79
Show file tree
Hide file tree
Showing 15 changed files with 361 additions and 40 deletions.
22 changes: 22 additions & 0 deletions src/controllers/createTableEntity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Request, Response } from "express";

import { createTableEntity } from "../services/createTableEntity.js";
import { CreateTableEntityParams } from "../types/openapi.js";

/**
* Update a specific entity
* @param req
* @param res
*/
export default async function (
req: Request<CreateTableEntityParams, null, Record<string, string>, unknown>,
res: Response<string>,
) {
try {
const result: string = await createTableEntity(req.body, req.params);
res.status(200).contentType("text/html").send(result);
} catch (error) {
console.error(error);
res.sendStatus(500);
}
}
33 changes: 33 additions & 0 deletions src/controllers/deleteTableEntity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Request, Response } from "express";

import { deleteTableEntity } from "../services/deleteTableEntity.js";
import {
DeleteTableEntityParams,
DeleteTableEntityQuery,
} from "../types/openapi.js";

/**
* Update a specific entity
* @param req
* @param res
*/
export default async function (
req: Request<
DeleteTableEntityParams,
null,
Record<string, string>,
DeleteTableEntityQuery
>,
res: Response<string>,
) {
try {
const result: string = await deleteTableEntity(
req.params,
req.query,
);
res.status(200).contentType("text/html").send(result);
} catch (error) {
console.error(error);
res.sendStatus(500);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Request, Response } from "express";

import { getTableEntity } from "../services/getTableEntity.js";
import { getTableCreateEntityPage } from "../services/getTableCreateEntityPage.js";
import { GetTableEntityParams, GetTableEntityQuery } from "../types/openapi.js";

/**
Expand All @@ -13,7 +13,9 @@ export default async function (
res: Response<string>,
) {
try {
const result: string = await getTableEntity(req.params, req.query);
const result: string = await getTableCreateEntityPage(
req.params,
);
res.status(200).contentType("text/html").send(result);
} catch (error) {
console.error(error);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Request, Response } from "express";

import { getTableEntities } from "../services/getTableEntities.js";
import { getTableEntitiesPage } from "../services/getTableEntitiesPage.js";
import {
GetTableEntitiesParams,
GetTableEntitiesQuery,
Expand All @@ -16,7 +16,7 @@ export default async function (
res: Response<string>,
) {
try {
const result: string = await getTableEntities(req.params, req.query);
const result: string = await getTableEntitiesPage(req.params, req.query);
res.status(200).contentType("text/html").send(result);
} catch (error) {
console.error(error);
Expand Down
25 changes: 25 additions & 0 deletions src/controllers/getTableUpdateEntityPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Request, Response } from "express";

import { getTableUpdateEntityPage } from "../services/getTableUpdateEntityPage.js";
import { GetTableEntityParams, GetTableEntityQuery } from "../types/openapi.js";

/**
* Retrieve a specific entity
* @param req
* @param res
*/
export default async function (
req: Request<GetTableEntityParams, null, unknown, GetTableEntityQuery>,
res: Response<string>,
) {
try {
const result: string = await getTableUpdateEntityPage(
req.params,
req.query,
);
res.status(200).contentType("text/html").send(result);
} catch (error) {
console.error(error);
res.sendStatus(500);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Request, Response } from "express";

import { getTables } from "../services/getTables.js";
import { getTablesPage } from "../services/getTablesPage.js";

/**
* Retrieve a list of available tables
Expand All @@ -12,7 +12,7 @@ export default async function (
res: Response<string>,
) {
try {
const result: string = await getTables();
const result: string = await getTablesPage();
res.status(200).contentType("text/html").send(result);
} catch (error) {
console.error(error);
Expand Down
20 changes: 13 additions & 7 deletions src/routes.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import express from "express";

import routeGetTableEntities from "./controllers/getTableEntities.js";
import routeGetTableEntity from "./controllers/getTableEntity.js";
import routeGetTables from "./controllers/getTables.js";
import routeCreateTableEntity from "./controllers/createTableEntity.js";
import routeDeleteTableEntity from "./controllers/deleteTableEntity.js";
import routeGetTableCreateEntityPage from "./controllers/getTableCreateEntityPage.js";
import routeGetTableEntitiesPage from "./controllers/getTableEntitiesPage.js";
import routeGetTablesPage from "./controllers/getTablesPage.js";
import routeGetTableUpdateEntityPage from "./controllers/getTableUpdateEntityPage.js";
import routeUpdateTableEntity from "./controllers/updateTableEntity.js";

const router = express.Router();

router.get("/tables/:tableName", routeGetTableEntities);
router.get("/tables/:tableName/entity", routeGetTableEntity);
router.get("/", routeGetTables);
router.post("/tables/:tableName/entity", routeUpdateTableEntity);
router.get("/tables/:tableName", routeGetTableEntitiesPage);
router.get("/tables/:tableName/create", routeGetTableCreateEntityPage);
router.get("/tables/:tableName/update", routeGetTableUpdateEntityPage);
router.get("/", routeGetTablesPage);
router.post("/tables/:tableName/create", routeCreateTableEntity);
router.post("/tables/:tableName/update", routeUpdateTableEntity);
router.post("/tables/:tableName/delete", routeDeleteTableEntity);

export default router;
49 changes: 49 additions & 0 deletions src/services/createTableEntity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import knex from "../config/database.js";
import { CreateTableEntityParams } from "../types/openapi.js";
import { getHtmlPageTemplate } from "../utils/html.js";

/**
* Create a new entity
* @param request
* @param params
*/
export async function createTableEntity(
request: Record<string, string>,
params: CreateTableEntityParams,
): Promise<string> {
const { tableName } = params;
const dataToInsert = request;

if (!tableName) {
throw new Error("Table name is required");
}

try {
const insertedId = await knex(tableName).insert(dataToInsert);

const entityCreatedData: string[] = [];
Object.keys(dataToInsert).forEach((key) => {
entityCreatedData.push(key + ": <b>" + dataToInsert[key] + "</b>");
});

const formHtml = `
<div>
<p><a href="/">Back to tables</a></p>
<p><a href="/tables/${tableName}">Back to table '${tableName}'</a></p>
</div>
<div>
<p>The entity has been created successfully:</p>
<ul>${entityCreatedData.map((value) => "<li>" + value + "</li>").join("")}</ul>
<p>Inserted ID: ${insertedId}</p>
</div>
`;

return getHtmlPageTemplate(
`Entity in table '${params.tableName}'`,
formHtml,
);
} catch (error) {
console.error("Error creating entity:", error);
throw error;
}
}
57 changes: 57 additions & 0 deletions src/services/deleteTableEntity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import knex from "../config/database.js";
import {
DeleteTableEntityParams,
DeleteTableEntityQuery,
} from "../types/openapi.js";
import { getHtmlPageTemplate } from "../utils/html.js";

/**
* Delete a specific entity
* @param params
* @param query
*/
export async function deleteTableEntity(
params: DeleteTableEntityParams,
query: DeleteTableEntityQuery,
): Promise<string> {
const { tableName } = params;
const primaryKeys = query;

if (!tableName) {
throw new Error("Table name is required");
}

try {
const deletedRows = await knex(tableName)
.where(primaryKeys)
.del();

if (deletedRows === 0) {
throw new Error("No rows deleted");
}

const entityId: string[] = [];
Object.keys(primaryKeys).forEach((primaryKey) => {
entityId.push(primaryKey + " " + primaryKeys[primaryKey]);
});

const formHtml = `
<div>
<p><a href="/">Back to tables</a></p>
<p><a href="/tables/${tableName}">Back to table '${tableName}'</a></p>
</div>
<div>
<p>The entity has been deleted successfully:</p>
<p>Entity ID: ${entityId.join(", ")}</p>
</div>
`;

return getHtmlPageTemplate(
`Entity '${entityId.join(", ")}' in table '${params.tableName}'`,
formHtml,
);
} catch (error) {
console.error("Error deleting entity:", error);
throw error;
}
}
107 changes: 107 additions & 0 deletions src/services/getTableCreateEntityPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import knex from "../config/database.js";
import { GetTableEntityParams } from "../types/openapi.js";
import { getColumns, tableCheck } from "../utils/db.js";
import { getHtmlPageTemplate } from "../utils/html.js";

/**
* Create a new entity
* @param params
*/
export async function getTableCreateEntityPage(
params: GetTableEntityParams,
): Promise<string> {
const { tableName } = params;

const tableColumns = await tableCheck(tableName);

const columns = await getColumns(knex, tableName || "table");

if (!columns) {
throw new Error("no such columns");
}

const autoIncrementColumns = columns
.filter((value) => value.Extra === "auto_increment")
.map((value) => value.Field);

const visibleColumns = columns
.filter(
(value) => value.Key === "PRI" || tableColumns.includes(value.Field),
)
.map((value) => value.Field);

const formHtml = `
<div>
<p><a href="/">Back to tables</a></p>
<p><a href="/tables/${tableName}">Back to table '${tableName}'</a></p>
</div>
<div>
<form action="/tables/${tableName}/create" method="post">
${visibleColumns
.filter((column) => tableColumns.includes(column))
.map((column) => {
const columnType = columns.find((c) => c.Field === column)?.Type;
let inputType = "text";
let inputAttributes = "";
let inputElement = "";
if (columnType == null) {
inputType = "text";
} else if (
columnType.includes("int") ||
columnType.includes("tinyint")
) {
inputType = "number";
} else if (
columnType.includes("datetime") ||
columnType.includes("timestamp")
) {
inputType = "datetime-local";
inputAttributes = `value="${new Date().toISOString().slice(0, 16)}"`;
inputElement = `
<input type="${inputType}" id="${column}" name="${column}" ${inputAttributes} ${autoIncrementColumns.includes(column) ? "disabled" : ""}>
`;
} else if (columnType.includes("enum")) {
const enumValues = columnType
.replace("enum(", "")
.replace(")", "")
.split(",");
inputAttributes = `value="${enumValues[0]}"`;
inputElement = `
<select id="${column}" name="${column}" ${autoIncrementColumns.includes(column) ? "disabled" : ""}>
${enumValues.map((value) => `<option value="${value}" ${value === enumValues[0] ? "selected" : ""}>${value}</option>`).join("")}
</select>
`;
} else if (columnType.includes("bool")) {
inputType = "checkbox";
inputAttributes = `value="1"`;
} else {
inputElement = `
<input type="${inputType}" id="${column}" name="${column}" value="" autocomplete="off" ${autoIncrementColumns.includes(column) ? "disabled" : ""} ${inputAttributes}>
`;
}
if (!inputElement) {
inputElement = `
<input type="${inputType}" id="${column}" name="${column}" autocomplete="off" ${autoIncrementColumns.includes(column) ? "disabled" : ""} ${inputAttributes}>
`;
}
return `
<div>
<p><label for="${column}">${column} - ${columnType}</label></p>
${inputElement}
</div>
`;
})
.join("")}
<button type="submit">Create</button>
</form>
</div>
`;

return getHtmlPageTemplate(
`Create new entity in table '${params.tableName}'`,
formHtml,
);
}
Loading

0 comments on commit 5ac6f79

Please sign in to comment.