Merged some functions in database/utils.ts to attempt to shorten code length, Also changed tests to work with the new datatypes
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/// <reference lib="deno.ns" />
|
||||
/**
|
||||
/**
|
||||
* @author Esad Mustafoski
|
||||
* @description Main API file, Handles all the routing/api stuff
|
||||
* Main API file, Handles all the routing/api stuff
|
||||
*/
|
||||
|
||||
// +++ IMPORTS ------------------------------------------------------ //
|
||||
@@ -9,12 +9,10 @@ import { Application, Router } from "https://deno.land/x/oak/mod.ts";
|
||||
import { oakCors } from "https://deno.land/x/cors/mod.ts";
|
||||
import * as db_utils from "../database/utils.ts";
|
||||
|
||||
|
||||
// +++ VARIABLES ---------------------------------------------------- //
|
||||
const router = new Router();
|
||||
const app = new Application();
|
||||
|
||||
|
||||
// +++ ROUTER ------------------------------------------------------- //
|
||||
// Creates the routes for the API server.
|
||||
// Example: localhost:8000/api will show "testAPIPoint"
|
||||
@@ -36,10 +34,9 @@ router
|
||||
ctx.response.body = { getPosts, countedPosts };
|
||||
});
|
||||
|
||||
|
||||
app.use(oakCors());
|
||||
app.use(router.routes());
|
||||
app.use(router.allowedMethods());
|
||||
|
||||
export { app };
|
||||
await app.listen({ port: 8000 });
|
||||
await app.listen({ port: 8000 });
|
||||
|
||||
@@ -1,5 +1,75 @@
|
||||
/// <reference lib="deno.ns" />
|
||||
/**
|
||||
/**
|
||||
* @author Esad Mustafoski
|
||||
* @description This file is responsible for creating the database and the tables
|
||||
*/
|
||||
*/
|
||||
|
||||
/// <reference lib="deno.ns" />
|
||||
|
||||
// +++ IMPORTS ------------------------------------------------------ //
|
||||
import { DB } from "https://deno.land/x/sqlite/mod.ts";
|
||||
import { dirname, fromFileUrl, join } from "https://deno.land/std/path/mod.ts";
|
||||
|
||||
// +++ VARIABLES ---------------------------------------------------- //
|
||||
const _dirname: string = dirname(fromFileUrl(import.meta.url));
|
||||
const dbPath: string = join(_dirname, "../database/esp-projekt.sqlite");
|
||||
const db = new DB(dbPath);
|
||||
|
||||
db.execute(`
|
||||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_group TEXT,
|
||||
bio TEXT,
|
||||
displayname TEXT,
|
||||
username TEXT,
|
||||
user_email TEXT,
|
||||
password TEXT,
|
||||
firstname TEXT,
|
||||
surname TEXT,
|
||||
account_created TEXT,
|
||||
blocked_users TEXT,
|
||||
followers TEXT,
|
||||
following TEXT,
|
||||
contacts TEXT
|
||||
)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS posts (
|
||||
posts_uuid INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER,
|
||||
created_at TEXT,
|
||||
post_text TEXT,
|
||||
likes INTEGER,
|
||||
comments INTEGER
|
||||
)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS comments (
|
||||
comment_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
post_id INTEGER,
|
||||
user_id INTEGER,
|
||||
created_at TEXT,
|
||||
comment_text TEXT
|
||||
)
|
||||
`);
|
||||
|
||||
// Sample data generated using online generators
|
||||
export function insertSampleData(): void {
|
||||
db.query(`INSERT INTO accounts (user_group, bio, displayname, username, user_email, password, firstname, surname, account_created, blocked_users, followers, following, contacts) VALUES
|
||||
('admin', 'Admin bio', 'Admin User', 'admin', 'admin@example.com', 'hashedpass1', 'Admin', 'User', '2024-01-01', '[]', '[]', '[]', '[]'),
|
||||
('user', 'I love coding!', 'John Dev', 'johndev', 'john@example.com', 'hashedpass2', 'John', 'Smith', '2024-01-02', '[]', '[3,4]', '[1,2]', '[]'),
|
||||
('user', 'Photography enthusiast', 'Alice', 'alice_photo', 'alice@example.com', 'hashedpass3', 'Alice', 'Johnson', '2024-01-03', '[5]', '[1]', '[2]', '[]')
|
||||
`);
|
||||
|
||||
db.query(`INSERT INTO posts (user_id, created_at, post_text, likes, comments) VALUES
|
||||
(1, '2024-01-15 10:00:00', 'First post about programming!', 5, 2),
|
||||
(1, '2024-01-15 11:30:00', 'Check out this new feature', 10, 3),
|
||||
(2, '2024-01-16 09:15:00', 'Just learned about TypeScript', 8, 1),
|
||||
(3, '2024-01-16 14:20:00', 'Posted my new photo collection', 15, 4)
|
||||
`);
|
||||
|
||||
db.query(`INSERT INTO comments (post_id, user_id, created_at, comment_text) VALUES
|
||||
(1, 2, '2024-01-15 10:05:00', 'Great post!'),
|
||||
(1, 3, '2024-01-15 10:10:00', 'Very informative'),
|
||||
(2, 3, '2024-01-15 11:35:00', 'Nice feature'),
|
||||
(3, 1, '2024-01-16 09:20:00', 'TypeScript is awesome'),
|
||||
(4, 2, '2024-01-16 14:25:00', 'Beautiful photos!')
|
||||
`);
|
||||
};
|
||||
@@ -1,18 +1,19 @@
|
||||
/**
|
||||
/**
|
||||
* @author Esad Mustafoski
|
||||
* @description This file is responsible for creating Functions to easily access the Database, Intended for use in the API
|
||||
*/
|
||||
|
||||
/// <reference lib="deno.ns" />
|
||||
|
||||
import { DB } from "https://deno.land/x/sqlite/mod.ts";
|
||||
// +++ IMPORTS ------------------------------------------------------ //
|
||||
import { DB, Row } from "https://deno.land/x/sqlite/mod.ts";
|
||||
import { dirname, fromFileUrl, join } from "https://deno.land/std/path/mod.ts";
|
||||
|
||||
// +++ VARIABLES ---------------------------------------------------- //
|
||||
// __dirname Is never getting used again, It's only needed because the DB Import
|
||||
// from SQLite doesn't like relative paths, so I use this as
|
||||
// A Workaround
|
||||
|
||||
// +++ VARIABLES ---------------------------------------------------- //
|
||||
const _dirname: string = dirname(fromFileUrl(import.meta.url));
|
||||
const dbPath: string = join(_dirname, "../database/esp-projekt.sqlite");
|
||||
const db = new DB(dbPath);
|
||||
@@ -45,17 +46,35 @@ interface Accounts {
|
||||
contacts: string;
|
||||
}
|
||||
|
||||
interface Comments {
|
||||
comment_id: number;
|
||||
post_id: number;
|
||||
author_user_id: number;
|
||||
date_created_at: string;
|
||||
text: string;
|
||||
likes: number;
|
||||
}
|
||||
|
||||
|
||||
// +++ FUNCTIONS---------------------------------------------------- //
|
||||
|
||||
/**
|
||||
* @param user_uuid The UUID of the User to get the Posts for. Not required
|
||||
* @description If no user_uuid is provided, all Posts will be returned
|
||||
* @description If a user_uuid is provided, only the Posts from that User will be returned
|
||||
* @returns Array of all Posts in the Database
|
||||
*/
|
||||
async function getPostsFromDB() {
|
||||
async function getPostsFromDB(user_uuid?: string): Promise<Post[]> {
|
||||
const data_result: Array<Post> = [];
|
||||
try {
|
||||
const rows = await db.query(`SELECT * FROM posts`);
|
||||
let rows: Row[];
|
||||
|
||||
try {
|
||||
if (user_uuid === undefined) {
|
||||
rows = await db.query(`SELECT * FROM posts`);
|
||||
} else {
|
||||
rows = await db.query(`SELECT * FROM posts WHERE user_id = ${user_uuid}`);
|
||||
}
|
||||
|
||||
// Assuming `db.query` returns an array of arrays or tuples
|
||||
for (const row of rows) {
|
||||
const [
|
||||
posts_uuid,
|
||||
@@ -67,12 +86,12 @@ async function getPostsFromDB() {
|
||||
] = row;
|
||||
|
||||
data_result.push({
|
||||
posts_uuid: Number(posts_uuid), // Convert to string if necessary
|
||||
posts_uuid: Number(posts_uuid),
|
||||
user_id: Number(user_id),
|
||||
created_at: String(created_at), // Convert to Date if necessary
|
||||
created_at: String(created_at),
|
||||
post_text: String(post_text),
|
||||
likes: Number(likes), // Convert to number if necessary
|
||||
comments: Number(comments), // Convert to number if necessary
|
||||
likes: Number(likes),
|
||||
comments: Number(comments),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -84,7 +103,7 @@ async function getPostsFromDB() {
|
||||
/**
|
||||
* @returns Array of all Users in the Database
|
||||
*/
|
||||
async function getAllUsersFromDB() {
|
||||
async function getAllUsersFromDB(): Promise<Accounts[]> {
|
||||
const accounts_list: Array<Accounts> = [];
|
||||
try {
|
||||
const rows = await db.query("SELECT * FROM accounts");
|
||||
@@ -131,6 +150,7 @@ async function getAllUsersFromDB() {
|
||||
|
||||
// Test Function, not useful
|
||||
// Promise needed because of "await"
|
||||
// It indicates that the function resolves something
|
||||
async function countPosts(): Promise<number> {
|
||||
let count = 0;
|
||||
try {
|
||||
@@ -145,10 +165,47 @@ async function countPosts(): Promise<number> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param post_id The ID of the Post to get the Comments for
|
||||
* @param post_id The ID of the Post to get the Comments for. Not required
|
||||
* @description If no post_id is provided, all Comments will be returned
|
||||
* @description If a post_id is provided, only the Comments for that Post will be returned
|
||||
* @returns Array of Comments for the Post, or an empty Array if there are no Comments
|
||||
*/
|
||||
function getCommentsForPost(post_id: number) {
|
||||
async function getCommentsFromDB(post_id?: number): Promise<Comments[]> {
|
||||
const data_result:Array<Comments> = [];
|
||||
let rows: Row[] = [];
|
||||
|
||||
try {
|
||||
if (post_id === undefined) {
|
||||
rows = await db.query(`SELECT * FROM comments`);
|
||||
} else {
|
||||
rows = await db.query(`SELECT * FROM comments WHERE post_id = ${post_id}`);
|
||||
}
|
||||
|
||||
for (const row of rows) {
|
||||
const [
|
||||
comment_id,
|
||||
post_id,
|
||||
author_user_id,
|
||||
date_created_at,
|
||||
text,
|
||||
likes,
|
||||
] = row;
|
||||
|
||||
data_result.push({
|
||||
comment_id: Number(comment_id),
|
||||
post_id: Number(post_id),
|
||||
author_user_id: Number(author_user_id),
|
||||
date_created_at: String(date_created_at),
|
||||
text: String(text),
|
||||
likes: Number(likes),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching comments", error, "Post ID:", post_id);
|
||||
return [];
|
||||
}
|
||||
|
||||
return data_result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,18 +217,16 @@ function getCommentsForComments(comment_id: number) {
|
||||
|
||||
/**
|
||||
* @param user_id The ID of the User to get
|
||||
* @returns The User with the given ID, or null if the User doesn't exist
|
||||
* @returns All of an users Data from the Database
|
||||
* Included: Comments, Posts... Everything including the specific user_uuid in the database
|
||||
*/
|
||||
function getUserInfoByID(user_id: number) {
|
||||
function getAllUserInfoByID(user_id: number) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param user_id The ID of the User to get the Posts for
|
||||
* @returns Array of Posts from the User, or an empty Array if the User doesn't exist or has no Posts
|
||||
*/
|
||||
function getAllPostsFromUser(user_id: number) {
|
||||
}
|
||||
|
||||
// Filter Functions
|
||||
function filterForImagePosts(posts_to_filter: Array<any>) {
|
||||
return [];
|
||||
@@ -185,16 +240,15 @@ function filterForTextPosts() {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Export all Functions to make this a module
|
||||
// Exporting all functions as this is a module
|
||||
export {
|
||||
countPosts,
|
||||
filterForImagePosts,
|
||||
filterForTextPosts,
|
||||
filterForVideoPosts,
|
||||
getAllPostsFromUser,
|
||||
getAllUsersFromDB,
|
||||
getCommentsForComments,
|
||||
getCommentsForPost,
|
||||
getCommentsFromDB,
|
||||
getPostsFromDB,
|
||||
getUserInfoByID,
|
||||
getAllUserInfoByID,
|
||||
};
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
// Responsible: Esad Mustafoski
|
||||
/**
|
||||
* @author Esad Mustafoski
|
||||
* @file test/api_main_test.ts
|
||||
* This file is made to test the API returns, Is not ready yet
|
||||
*/
|
||||
|
||||
// test/api_main_test.ts
|
||||
/// <reference lib="deno.ns" />
|
||||
// GENERATED USING AI, DO NOT USE YET
|
||||
|
||||
// +++ IMPORTS ------------------------------------------------------ //
|
||||
import { superoak } from "https://deno.land/x/superoak/mod.ts";
|
||||
import { app } from "../api/main.ts";
|
||||
|
||||
// +++ TESTS ------------------------------------------------------- //
|
||||
Deno.test("GET /api returns testAPIPoint", async () => {
|
||||
const request = await superoak(app);
|
||||
await request.get("/api").expect(200).expect("testAPIPoint");
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
// Responsible: Esad Mustafoski
|
||||
/**
|
||||
* @author Esad Mustafoski
|
||||
* @file db.test.ts
|
||||
* This file is made to test the Database Functions
|
||||
*/
|
||||
|
||||
/// <reference lib="deno.ns" />
|
||||
// GENERATED USING AI, DO NOT USE YET
|
||||
|
||||
// +++ IMPORTS ------------------------------------------------------ //
|
||||
import {
|
||||
assert,
|
||||
assertEquals,
|
||||
@@ -10,6 +15,7 @@ import {
|
||||
|
||||
import * as db_utils from "../database/utils.ts";
|
||||
|
||||
// +++ TESTS ------------------------------------------------------- //
|
||||
// Database Tests
|
||||
Deno.test("Database Connection", async () => {
|
||||
const users = await db_utils.getAllUsersFromDB();
|
||||
@@ -20,12 +26,15 @@ Deno.test("Database Connection", async () => {
|
||||
Deno.test("getAllUsersFromDB returns array of users with correct properties", async () => {
|
||||
const users = await db_utils.getAllUsersFromDB();
|
||||
assert(Array.isArray(users), "Expected users to be an array");
|
||||
|
||||
|
||||
if (users.length > 0) {
|
||||
const user = users[0];
|
||||
assert(typeof user.user_id === "number", "user_id should be a number");
|
||||
assert(typeof user.username === "string", "username should be a string");
|
||||
assert(typeof user.user_email === "string", "user_email should be a string");
|
||||
const user = users[0];
|
||||
assert(typeof user.user_id === "number", "user_id should be a number");
|
||||
assert(typeof user.username === "string", "username should be a string");
|
||||
assert(
|
||||
typeof user.user_email === "string",
|
||||
"user_email should be a string",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -33,13 +42,16 @@ Deno.test("getAllUsersFromDB returns array of users with correct properties", as
|
||||
Deno.test("getPostsFromDB returns array of posts with correct structure", async () => {
|
||||
const posts = await db_utils.getPostsFromDB();
|
||||
assert(Array.isArray(posts), "Expected posts to be an array");
|
||||
|
||||
|
||||
if (posts.length > 0) {
|
||||
const post = posts[0];
|
||||
assert(typeof post.posts_uuid === "number", "posts_uuid should be a number");
|
||||
assert(typeof post.user_id === "number", "user_id should be a number");
|
||||
assert(typeof post.post_text === "string", "post_text should be a string");
|
||||
assert(typeof post.likes === "number", "likes should be a number");
|
||||
const post = posts[0];
|
||||
assert(
|
||||
typeof post.posts_uuid === "number",
|
||||
"posts_uuid should be a number",
|
||||
);
|
||||
assert(typeof post.user_id === "number", "user_id should be a number");
|
||||
assert(typeof post.post_text === "string", "post_text should be a string");
|
||||
assert(typeof post.likes === "number", "likes should be a number");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -47,12 +59,12 @@ Deno.test("countPosts returns valid number", async () => {
|
||||
const count = await db_utils.countPosts();
|
||||
assert(typeof count === "number", "Count should be a number");
|
||||
assert(count >= 0, "Count should be non-negative");
|
||||
|
||||
|
||||
const posts = await db_utils.getPostsFromDB();
|
||||
assertEquals(count, posts.length, "Count should match number of posts");
|
||||
});
|
||||
|
||||
// Filter Tests
|
||||
// Filter Tests (Will not work until the functions are implemented)
|
||||
Deno.test("filterForImagePosts returns array", () => {
|
||||
const mockPosts: Array<any> = [];
|
||||
const result = db_utils.filterForImagePosts(mockPosts);
|
||||
@@ -81,43 +93,47 @@ Deno.test("getPostsFromDB handles errors gracefully", async () => {
|
||||
});
|
||||
|
||||
// Comments Tests
|
||||
Deno.test("getCommentsForPost handles invalid post_id", () => {
|
||||
const result = db_utils.getCommentsForPost(-1);
|
||||
assertEquals(result, undefined, "Should handle invalid post_id");
|
||||
Deno.test("getCommentsFromDB handles invalid post_id", async () => {
|
||||
const result = await db_utils.getCommentsFromDB(-1);
|
||||
assertEquals(result, [], "Should handle invalid post_id");
|
||||
});
|
||||
|
||||
Deno.test("getCommentsForComments handles invalid comment_id", () => {
|
||||
Deno.test("getCommentsFromDB handles invalid comment_id", () => {
|
||||
const result = db_utils.getCommentsForComments(-1);
|
||||
assertEquals(result, undefined, "Should handle invalid comment_id");
|
||||
});
|
||||
|
||||
// User Info Tests
|
||||
Deno.test("getUserInfoByID handles invalid user_id", () => {
|
||||
const result = db_utils.getUserInfoByID(-1);
|
||||
const result = db_utils.getAllUserInfoByID(-1);
|
||||
assertEquals(result, undefined, "Should handle invalid user_id");
|
||||
});
|
||||
|
||||
Deno.test("getAllPostsFromUser handles invalid user_id", () => {
|
||||
const result = db_utils.getAllPostsFromUser(-1);
|
||||
assertEquals(result, undefined, "Should handle invalid user_id");
|
||||
Deno.test("getPostsFromDB handles invalid user_id", async () => {
|
||||
const result = await db_utils.getPostsFromDB("invalid");
|
||||
assertEquals(result, [], "Should handle invalid user_id");
|
||||
});
|
||||
|
||||
// Data Validation Tests
|
||||
Deno.test("User data types are correct", async () => {
|
||||
const users = await db_utils.getAllUsersFromDB();
|
||||
if (users.length > 0) {
|
||||
const user = users[0];
|
||||
assertMatch(user.user_email, /^[^\s@]+@[^\s@]+\.[^\s@]+$/, "Email should be valid format");
|
||||
assert(user.password.length >= 1, "Password should not be empty");
|
||||
const user = users[0];
|
||||
assertMatch(
|
||||
user.user_email,
|
||||
/^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
||||
"Email should be valid format",
|
||||
);
|
||||
assert(user.password.length >= 1, "Password should not be empty");
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("Post data types are correct", async () => {
|
||||
const posts = await db_utils.getPostsFromDB();
|
||||
if (posts.length > 0) {
|
||||
const post = posts[0];
|
||||
assert(post.likes >= 0, "Likes should be non-negative");
|
||||
assert(post.comments >= 0, "Comments should be non-negative");
|
||||
assert(post.post_text.length > 0, "Post text should not be empty");
|
||||
const post = posts[0];
|
||||
assert(post.likes >= 0, "Likes should be non-negative");
|
||||
assert(post.comments >= 0, "Comments should be non-negative");
|
||||
assert(post.post_text.length > 0, "Post text should not be empty");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user