diff --git a/src/api.test.ts b/src/api.test.ts index 34ae3c2..0e718ef 100644 --- a/src/api.test.ts +++ b/src/api.test.ts @@ -3,6 +3,9 @@ import authtoken from "../secrets/user1-accesstoken.json"; const token = authtoken["sha1"]; const username = authtoken["login"]; +const repo = authtoken["repo"]; +const api = new Forgejo("http://localhost:3000", "owner_user"); +api.setTokenAuth(token); test("use authentication without setting it ", () => { const api = new Forgejo("http://localhost:3000", "owner_user"); @@ -15,9 +18,50 @@ test("use authentication without setting it ", () => { }); test("verify /user API ", async () => { - const api = new Forgejo("http://localhost:3000", "owner_user"); - api.setTokenAuth(token); let user = await api.getUser(); - console.log(user); expect(user["login"]).toBe(username); }); + +test("notifications API ", async () => { + let notifications = await api.getNotifications(); + expect(notifications.length).toBe(5); + expect(await api.getNumUnreadNotifications()).toBe(5); + + let notification = await api.getNotificationThread(notifications[0].id); + expect(notification).toEqual(notifications[0]); +}); + +test("issue API ", async () => { + let notifications = await api.getNotifications(); + let issue = await api.getIssue(username, repo, 1); + expect(notifications[0].subject.type).toEqual("Issue"); + expect(issue.title).toEqual(notifications[0].subject.title); + expect(issue.lazy_comments_data).toBeUndefined(); + expect(issue.created_at instanceof Date).toBe(true); + expect(issue.updated_at instanceof Date).toBe(true); + if (issue.closed_at) { + // TODO: add issue closing functionality in integration/forgejo.py and verify this + expect(issue.closed_at instanceof Date).toBe(true); + } + // expect(typeof(issue.created_at)).toBe('Date'); +}); + +test("comments API ", async () => { + let notifications = await api.getNotifications(); + let issue = await api.getIssue(username, repo, 1); + issue = await api.getCommentsForIssue(issue); + expect(issue.lazy_comments_data.length).toBe(2); + + issue.lazy_comments_data?.forEach((comment) => { + expect(comment.created_at instanceof Date).toBe(true); + expect(comment.updated_at instanceof Date).toBe(true); + }); +}); + +test("mentions in issue", async () => { + expect(await api.findMentionsInIssue(username, repo, 1)).toBe(true); + expect(await api.findMentionsInIssue(username, repo, 2)).toBe(true); + expect(await api.findMentionsInIssue(username, repo, 3)).toBe(true); + expect(await api.findMentionsInIssue(username, repo, 4)).toBe(true); + expect(await api.findMentionsInIssue(username, repo, 5)).toBe(false); +}); diff --git a/src/api.ts b/src/api.ts index e560286..77df56c 100644 --- a/src/api.ts +++ b/src/api.ts @@ -3,6 +3,10 @@ // SPDX-License-Identifier: AGPL-3.0-or-later import Auth from "./auth"; +import User from "./spec/user"; +import Notification from "./spec/notification"; +import Issue from "./spec/issue"; +import Comment from "./spec/comments"; class Forgejo { url: URL; @@ -16,7 +20,6 @@ class Forgejo { */ constructor(url: string, username: string) { this.url = new URL(url); - console.log(this.url.toString()); this.username = username; } @@ -43,69 +46,85 @@ class Forgejo { /** * Get logged in user */ - async getUser() { + async getUser(): Promise { this.url.pathname = "/api/v1/user"; - console.log(this.url.toString()); let res = await fetch(this.url, { method: "GET", credentials: "include", headers: this.getTokenAuthHeader(), }); - console.log(`got res: ${res}`); return await res.json(); } /** * Get all notifications */ - async getNotifications() { + async getNotifications(): Promise> { this.url.pathname = "/api/v1/notifications"; - let res = await fetch(this.url); + let res = await fetch(this.url, { + method: "GET", + credentials: "include", + headers: this.getTokenAuthHeader(), + }); + return await res.json(); } /** * Get number of unread notifications */ - async getNumUnreadNotifications() { + async getNumUnreadNotifications(): Promise { this.url.pathname = "/api/v1/notifications/new"; - let res = await fetch(this.url); - return await res.json()["new"]; + let res = await fetch(this.url, { + method: "GET", + credentials: "include", + headers: this.getTokenAuthHeader(), + }); + + let data = await res.json(); + return data["new"]; } /** * Get Notification Thread * @param {number} id - The ID of a notification thread */ - async getNotificationThread(id: number) { + async getNotificationThread(id: number): Promise { this.url.pathname = `/api/v1/notifications/threads/${id}`; - let res = await fetch(this.url); + let res = await fetch(this.url, { + method: "GET", + credentials: "include", + headers: this.getTokenAuthHeader(), + }); return await res.json(); } /** * Get the time at which the issue was last read */ - lastReadTime(issue: number): number { - return 10120123; // TODO: return last read UNIX epoch time from storage. If unread, return 0 + lastReadTime(issue: Issue): Date { + return new Date("01/01/01"); // TODO: return last read time from storage. If unread, return 0 } /** * Check if logged in user is mentioned in the issue */ async findMentionsInIssue(owner: string, repo: string, id: number) { - let issue = this.getIssue(owner, repo, id); + let issue = await this.getIssue(owner, repo, id); + issue = await this.getCommentsForIssue(issue); let unreadTime = this.lastReadTime(issue); - const mentionUtil = (str: string): boolean => str.includes(this.username); + const mentionUtil = (str: string): boolean => { + return str.includes(this.username); + }; const items = []; - if (issue.created > unreadTime) { + if (new Date(issue.created_at) > unreadTime) { items.push(issue.title); items.push(issue.body); } - issue.commentsData.forEach((comment) => { + issue.lazy_comments_data.forEach((comment) => { if (comment.created_at > unreadTime) { items.push(comment.body); return; @@ -118,12 +137,14 @@ class Forgejo { } }); + let mention = false; items.forEach((item) => { if (mentionUtil(item)) { - return true; + mention = true; + return; } }); - return false; + return mention; } /** @@ -151,7 +172,62 @@ class Forgejo { * @param {str} repo - The name of the repository * @param {number} id - The ID of an issue */ - async getIssue(owner: string, repo: string, id: number) {} + async getIssue(owner: string, repo: string, id: number): Promise { + this.url.pathname = `/api/v1/repos/${owner}/${repo}/issues/${id}`; + let res = await fetch(this.url, { + method: "GET", + credentials: "include", + headers: this.getTokenAuthHeader(), + }); + let issue = await res.json(); + + if (typeof issue.created_at === "string") { + issue.created_at = new Date(issue.created_at); + } + + if (issue.updated_at) { + if (typeof issue.updated_at === "string") { + issue.updated_at = new Date(issue.updated_at); + } + } + + if (issue.closed_at) { + if (typeof issue.closed_at === "string") { + issue.closed_at = new Date(issue.closed_at); + } + } + + return issue; + } + + async getCommentsForIssue(issue: Issue): Promise { + // TODO: check if issue.number != issue.id causes problems. I'm assuming + // Issue.number is the local repository issue ID and issue.id is DB ID + this.url.pathname = `/api/v1/repos/${issue.repository.owner}/${issue.repository.name}/issues/${issue.number}/comments`; + let res = await fetch(this.url, { + method: "GET", + credentials: "include", + headers: this.getTokenAuthHeader(), + }); + let c: Array = await res.json(); + + c = c.map((comment) => { + if (typeof comment.created_at === "string") { + comment.created_at = new Date(comment.created_at); + } + + if (comment.updated_at) { + if (typeof comment.updated_at === "string") { + comment.updated_at = new Date(comment.updated_at); + } + } + return comment; + }); + + issue.lazy_comments_data = c; + + return issue; + } } export default Forgejo;