From ca8199ca33f73ff4b48384819ab42bc1a10c0240 Mon Sep 17 00:00:00 2001 From: Ravinou Date: Sat, 5 Apr 2025 10:53:13 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20=E2=9A=A1=20cleaning=20up=20the=20c?= =?UTF-8?q?entralisation=20of=20json=20reading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- helpers/functions/fileHelpers.ts | 1 - pages/api/account/getAppriseAlert.ts | 18 ++----- pages/api/account/getAppriseMode.ts | 18 ++----- pages/api/account/getAppriseServices.ts | 18 ++----- pages/api/account/getEmailAlert.ts | 17 ++----- pages/api/account/sendTestApprise.ts | 18 ++----- pages/api/auth/[...nextauth].ts | 16 ++----- tests/supertest/getAppriseAlert.test.ts | 32 ++++++------- tests/supertest/getAppriseMode.test.ts | 56 +++++++++++----------- tests/supertest/getAppriseServices.test.ts | 35 +++++++------- tests/supertest/getEmailAlert.test.ts | 25 +++++----- 11 files changed, 103 insertions(+), 151 deletions(-) diff --git a/helpers/functions/fileHelpers.ts b/helpers/functions/fileHelpers.ts index 16f148f..dd5cb29 100644 --- a/helpers/functions/fileHelpers.ts +++ b/helpers/functions/fileHelpers.ts @@ -1,6 +1,5 @@ import { promises as fs } from 'fs'; import path from 'path'; -import { Optional } from '~/types'; import { BorgWarehouseUser, Repository } from '~/types/domain/config.types'; import repoHistory from './repoHistory'; diff --git a/pages/api/account/getAppriseAlert.ts b/pages/api/account/getAppriseAlert.ts index 993ffe0..debcbfb 100644 --- a/pages/api/account/getAppriseAlert.ts +++ b/pages/api/account/getAppriseAlert.ts @@ -1,12 +1,9 @@ -//Lib -import { promises as fs } from 'fs'; -import path from 'path'; -import { authOptions } from '../auth/[...nextauth]'; -import { getServerSession } from 'next-auth/next'; import { NextApiRequest, NextApiResponse } from 'next'; -import { BorgWarehouseUser } from '~/types/domain/config.types'; -import { AppriseAlertResponse } from '~/types/api/notification.types'; +import { getServerSession } from 'next-auth/next'; +import { getUsersList } from '~/helpers/functions'; import { ErrorResponse } from '~/types/api/error.types'; +import { AppriseAlertResponse } from '~/types/api/notification.types'; +import { authOptions } from '../auth/[...nextauth]'; export default async function handler( req: NextApiRequest, @@ -25,12 +22,7 @@ export default async function handler( } try { - //Read the users file - const jsonDirectory = path.join(process.cwd(), '/config'); - const fileContent = await fs.readFile(jsonDirectory + '/users.json', 'utf8'); - - //Parse the usersList - const usersList: Array = JSON.parse(fileContent); + const usersList = await getUsersList(); //Verify that the user of the session exists const user = usersList.find((u) => u.username === session.user?.name); diff --git a/pages/api/account/getAppriseMode.ts b/pages/api/account/getAppriseMode.ts index 76eabd8..9845287 100644 --- a/pages/api/account/getAppriseMode.ts +++ b/pages/api/account/getAppriseMode.ts @@ -1,12 +1,9 @@ -//Lib -import { promises as fs } from 'fs'; -import path from 'path'; -import { authOptions } from '../auth/[...nextauth]'; -import { getServerSession } from 'next-auth/next'; import { NextApiRequest, NextApiResponse } from 'next'; -import { AppriseModeDTO } from '~/types/api/notification.types'; +import { getServerSession } from 'next-auth/next'; +import { getUsersList } from '~/helpers/functions'; import { ErrorResponse } from '~/types/api/error.types'; -import { BorgWarehouseUser } from '~/types/domain/config.types'; +import { AppriseModeDTO } from '~/types/api/notification.types'; +import { authOptions } from '../auth/[...nextauth]'; export default async function handler( req: NextApiRequest, @@ -23,12 +20,7 @@ export default async function handler( return; } try { - //Read the users file - const jsonDirectory = path.join(process.cwd(), '/config'); - const fileContent = await fs.readFile(jsonDirectory + '/users.json', 'utf8'); - - //Parse the usersList - const usersList: Array = JSON.parse(fileContent); + const usersList = await getUsersList(); //Verify that the user of the session exists const user = usersList.find((u) => u.username === session.user?.name); diff --git a/pages/api/account/getAppriseServices.ts b/pages/api/account/getAppriseServices.ts index fbc376e..28aa498 100644 --- a/pages/api/account/getAppriseServices.ts +++ b/pages/api/account/getAppriseServices.ts @@ -1,12 +1,9 @@ -//Lib -import { promises as fs } from 'fs'; -import path from 'path'; -import { authOptions } from '../auth/[...nextauth]'; -import { getServerSession } from 'next-auth/next'; import { NextApiRequest, NextApiResponse } from 'next'; -import { AppriseServicesDTO } from '~/types/api/notification.types'; +import { getServerSession } from 'next-auth/next'; +import { getUsersList } from '~/helpers/functions'; import { ErrorResponse } from '~/types/api/error.types'; -import { BorgWarehouseUser } from '~/types/domain/config.types'; +import { AppriseServicesDTO } from '~/types/api/notification.types'; +import { authOptions } from '../auth/[...nextauth]'; export default async function handler( req: NextApiRequest, @@ -23,12 +20,7 @@ export default async function handler( return; } try { - //Read the users file - const jsonDirectory = path.join(process.cwd(), '/config'); - const fileContent = await fs.readFile(jsonDirectory + '/users.json', 'utf8'); - - //Parse the usersList - const usersList: Array = JSON.parse(fileContent); + const usersList = await getUsersList(); //Verify that the user of the session exists const user = usersList.find((u) => u.username === session.user?.name); diff --git a/pages/api/account/getEmailAlert.ts b/pages/api/account/getEmailAlert.ts index ea68acb..2090dce 100644 --- a/pages/api/account/getEmailAlert.ts +++ b/pages/api/account/getEmailAlert.ts @@ -1,12 +1,10 @@ //Lib -import { promises as fs } from 'fs'; -import path from 'path'; -import { authOptions } from '../auth/[...nextauth]'; -import { getServerSession } from 'next-auth/next'; import { NextApiRequest, NextApiResponse } from 'next'; -import { EmailAlertDTO } from '~/types/api/notification.types'; +import { getServerSession } from 'next-auth/next'; +import { getUsersList } from '~/helpers/functions'; import { ErrorResponse } from '~/types/api/error.types'; -import { BorgWarehouseUser } from '~/types/domain/config.types'; +import { EmailAlertDTO } from '~/types/api/notification.types'; +import { authOptions } from '../auth/[...nextauth]'; export default async function handler( req: NextApiRequest, @@ -25,12 +23,7 @@ export default async function handler( } try { - //Read the users file - const jsonDirectory = path.join(process.cwd(), '/config'); - const fileContent = await fs.readFile(jsonDirectory + '/users.json', 'utf8'); - - //Parse the usersList - const usersList: Array = JSON.parse(fileContent); + const usersList = await getUsersList(); //Verify that the user of the session exists const user = usersList.find((u) => u.username === session.user?.name); diff --git a/pages/api/account/sendTestApprise.ts b/pages/api/account/sendTestApprise.ts index 1ecd86d..0415065 100644 --- a/pages/api/account/sendTestApprise.ts +++ b/pages/api/account/sendTestApprise.ts @@ -1,21 +1,13 @@ -import { authOptions } from '../auth/[...nextauth]'; -import { getServerSession } from 'next-auth/next'; -import { NextApiRequest, NextApiResponse } from 'next'; -import { promises as fs } from 'fs'; -import path from 'path'; -import { ErrorResponse, SuccessResponse } from '~/types/api/error.types'; -import { BorgWarehouseUser } from '~/types/domain/config.types'; import { exec } from 'child_process'; +import { NextApiRequest, NextApiResponse } from 'next'; +import { getServerSession } from 'next-auth/next'; import { promisify } from 'util'; +import { getUsersList } from '~/helpers/functions'; +import { ErrorResponse, SuccessResponse } from '~/types/api/error.types'; +import { authOptions } from '../auth/[...nextauth]'; const execAsync = promisify(exec); -const getUsersList = async (): Promise => { - const jsonDirectory = path.join(process.cwd(), '/config'); - const fileContent = await fs.readFile(`${jsonDirectory}/users.json`, 'utf8'); - return JSON.parse(fileContent); -}; - const getAppriseServicesURLs = (services: string[]): string => services.join(' '); const checkAppriseInstalled = async (): Promise => { diff --git a/pages/api/auth/[...nextauth].ts b/pages/api/auth/[...nextauth].ts index 8c3eafa..6b27722 100644 --- a/pages/api/auth/[...nextauth].ts +++ b/pages/api/auth/[...nextauth].ts @@ -1,10 +1,9 @@ -//Lib +import fs from 'fs'; import NextAuth, { NextAuthOptions, RequestInternal, User } from 'next-auth'; import CredentialsProvider from 'next-auth/providers/credentials'; -import { verifyPassword } from '../../../helpers/functions/auth'; -import fs from 'fs'; import path from 'path'; -import { BorgWarehouseUser } from '~/types/domain/config.types'; +import { getUsersList } from '~/helpers/functions'; +import { verifyPassword } from '../../../helpers/functions/auth'; const logLogin = async (message: string, req: Partial, success = false) => { const ipAddress = req.headers?.['x-forwarded-for'] || 'unknown'; @@ -54,14 +53,7 @@ export const authOptions: NextAuthOptions = { ); } - const usersData = await fs.promises.readFile(jsonDirectory + '/users.json', 'utf8'); - const usersList = (() => { - try { - return JSON.parse(usersData) as BorgWarehouseUser[]; - } catch (error) { - throw new Error('Failed to parse users.json. Please check its format.'); - } - })(); + const usersList = await getUsersList(); //Step 1 : does the user exist ? const userIndex = usersList.map((user) => user.username).indexOf(username.toLowerCase()); diff --git a/tests/supertest/getAppriseAlert.test.ts b/tests/supertest/getAppriseAlert.test.ts index 66721ec..a99d8e7 100644 --- a/tests/supertest/getAppriseAlert.test.ts +++ b/tests/supertest/getAppriseAlert.test.ts @@ -1,18 +1,16 @@ -import { createMocks } from 'node-mocks-http'; -import handler from '~/pages/api/account/getAppriseAlert'; import { getServerSession } from 'next-auth/next'; -import { promises as fs } from 'fs'; -import path from 'path'; +import { createMocks } from 'node-mocks-http'; +import { getUsersList } from '~/helpers/functions'; +import handler from '~/pages/api/account/getAppriseAlert'; jest.mock('next-auth/next'); -jest.mock('fs', () => ({ - promises: { - readFile: jest.fn(), - }, +jest.mock('~/helpers/functions/fileHelpers', () => ({ + getUsersList: jest.fn(), })); describe('Get Apprise Alert API', () => { beforeEach(() => { + jest.clearAllMocks(); jest.spyOn(console, 'log').mockImplementation(() => {}); }); @@ -35,9 +33,7 @@ describe('Get Apprise Alert API', () => { user: { name: 'nonexistent' }, }); - (fs.readFile as jest.Mock).mockResolvedValue( - JSON.stringify([{ username: 'testuser', appriseAlert: true }]) - ); + (getUsersList as jest.Mock).mockResolvedValue([{ username: 'testuser', appriseAlert: true }]); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); @@ -53,10 +49,7 @@ describe('Get Apprise Alert API', () => { user: { name: 'testuser' }, }); - (fs.readFile as jest.Mock).mockResolvedValue( - JSON.stringify([{ username: 'testuser', appriseAlert: true }]) - ); - + (getUsersList as jest.Mock).mockResolvedValue([{ username: 'testuser', appriseAlert: true }]); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); @@ -69,12 +62,17 @@ describe('Get Apprise Alert API', () => { user: { name: 'testuser' }, }); - (fs.readFile as jest.Mock).mockRejectedValue({ code: 'ENOENT' }); + (getUsersList as jest.Mock).mockImplementation(() => { + throw new Error(); + }); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); expect(res._getStatusCode()).toBe(500); - expect(res._getJSONData()).toEqual({ status: 500, message: 'No such file or directory' }); + expect(res._getJSONData()).toEqual({ + status: 500, + message: 'API error, contact the administrator', + }); }); }); diff --git a/tests/supertest/getAppriseMode.test.ts b/tests/supertest/getAppriseMode.test.ts index b883077..d4490c9 100644 --- a/tests/supertest/getAppriseMode.test.ts +++ b/tests/supertest/getAppriseMode.test.ts @@ -1,18 +1,16 @@ -import { createMocks } from 'node-mocks-http'; -import handler from '~/pages/api/account/getAppriseMode'; import { getServerSession } from 'next-auth/next'; -import { promises as fs } from 'fs'; -import path from 'path'; +import { createMocks } from 'node-mocks-http'; +import { getUsersList } from '~/helpers/functions'; +import handler from '~/pages/api/account/getAppriseMode'; jest.mock('next-auth/next'); -jest.mock('fs', () => ({ - promises: { - readFile: jest.fn(), - }, +jest.mock('~/helpers/functions/fileHelpers', () => ({ + getUsersList: jest.fn(), })); describe('Get Apprise Mode API', () => { beforeEach(() => { + jest.clearAllMocks(); jest.spyOn(console, 'log').mockImplementation(() => {}); }); @@ -35,15 +33,13 @@ describe('Get Apprise Mode API', () => { user: { name: 'nonexistent' }, }); - (fs.readFile as jest.Mock).mockResolvedValue( - JSON.stringify([ - { - username: 'testuser', - appriseMode: 'stateless', - appriseStatelessURL: 'https://example.com', - }, - ]) - ); + (getUsersList as jest.Mock).mockResolvedValue([ + { + username: 'testuser', + appriseMode: 'stateless', + appriseStatelessURL: 'https://example.com', + }, + ]); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); @@ -59,15 +55,13 @@ describe('Get Apprise Mode API', () => { user: { name: 'testuser' }, }); - (fs.readFile as jest.Mock).mockResolvedValue( - JSON.stringify([ - { - username: 'testuser', - appriseMode: 'stateless', - appriseStatelessURL: 'https://example.com', - }, - ]) - ); + (getUsersList as jest.Mock).mockResolvedValue([ + { + username: 'testuser', + appriseMode: 'stateless', + appriseStatelessURL: 'https://example.com', + }, + ]); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); @@ -84,12 +78,16 @@ describe('Get Apprise Mode API', () => { user: { name: 'testuser' }, }); - (fs.readFile as jest.Mock).mockRejectedValue({ code: 'ENOENT' }); - + (getUsersList as jest.Mock).mockImplementation(() => { + throw new Error(); + }); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); expect(res._getStatusCode()).toBe(500); - expect(res._getJSONData()).toEqual({ status: 500, message: 'No such file or directory' }); + expect(res._getJSONData()).toEqual({ + status: 500, + message: 'API error, contact the administrator', + }); }); }); diff --git a/tests/supertest/getAppriseServices.test.ts b/tests/supertest/getAppriseServices.test.ts index bf92f63..c40b054 100644 --- a/tests/supertest/getAppriseServices.test.ts +++ b/tests/supertest/getAppriseServices.test.ts @@ -1,18 +1,16 @@ -import { createMocks } from 'node-mocks-http'; -import handler from '~/pages/api/account/getAppriseServices'; import { getServerSession } from 'next-auth/next'; -import { promises as fs } from 'fs'; -import path from 'path'; +import { createMocks } from 'node-mocks-http'; +import { getUsersList } from '~/helpers/functions'; +import handler from '~/pages/api/account/getAppriseServices'; jest.mock('next-auth/next'); -jest.mock('fs', () => ({ - promises: { - readFile: jest.fn(), - }, +jest.mock('~/helpers/functions/fileHelpers', () => ({ + getUsersList: jest.fn(), })); describe('Get Apprise Services API', () => { beforeEach(() => { + jest.clearAllMocks(); jest.spyOn(console, 'log').mockImplementation(() => {}); }); @@ -35,9 +33,9 @@ describe('Get Apprise Services API', () => { user: { name: 'nonexistent' }, }); - (fs.readFile as jest.Mock).mockResolvedValue( - JSON.stringify([{ username: 'testuser', appriseServices: ['service1', 'service2'] }]) - ); + (getUsersList as jest.Mock).mockResolvedValue([ + { username: 'testuser', appriseServices: ['service1', 'service2'] }, + ]); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); @@ -53,9 +51,9 @@ describe('Get Apprise Services API', () => { user: { name: 'testuser' }, }); - (fs.readFile as jest.Mock).mockResolvedValue( - JSON.stringify([{ username: 'testuser', appriseServices: ['service1', 'service2'] }]) - ); + (getUsersList as jest.Mock).mockResolvedValue([ + { username: 'testuser', appriseServices: ['service1', 'service2'] }, + ]); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); @@ -71,12 +69,17 @@ describe('Get Apprise Services API', () => { user: { name: 'testuser' }, }); - (fs.readFile as jest.Mock).mockRejectedValue({ code: 'ENOENT' }); + (getUsersList as jest.Mock).mockImplementation(() => { + throw new Error(); + }); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); expect(res._getStatusCode()).toBe(500); - expect(res._getJSONData()).toEqual({ status: 500, message: 'No such file or directory' }); + expect(res._getJSONData()).toEqual({ + status: 500, + message: 'API error, contact the administrator', + }); }); }); diff --git a/tests/supertest/getEmailAlert.test.ts b/tests/supertest/getEmailAlert.test.ts index 392db76..4ec6cad 100644 --- a/tests/supertest/getEmailAlert.test.ts +++ b/tests/supertest/getEmailAlert.test.ts @@ -3,16 +3,16 @@ import handler from '~/pages/api/account/getEmailAlert'; import { getServerSession } from 'next-auth/next'; import { promises as fs } from 'fs'; import path from 'path'; +import { getUsersList } from '~/helpers/functions'; jest.mock('next-auth/next'); -jest.mock('fs', () => ({ - promises: { - readFile: jest.fn(), - }, +jest.mock('~/helpers/functions/fileHelpers', () => ({ + getUsersList: jest.fn(), })); describe('Get Email Alert API', () => { beforeEach(() => { + jest.clearAllMocks(); jest.spyOn(console, 'log').mockImplementation(() => {}); }); @@ -35,9 +35,7 @@ describe('Get Email Alert API', () => { user: { name: 'nonexistent' }, }); - (fs.readFile as jest.Mock).mockResolvedValue( - JSON.stringify([{ username: 'testuser', emailAlert: true }]) - ); + (getUsersList as jest.Mock).mockResolvedValue([{ username: 'testuser', emailAlert: true }]); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); @@ -53,9 +51,7 @@ describe('Get Email Alert API', () => { user: { name: 'testuser' }, }); - (fs.readFile as jest.Mock).mockResolvedValue( - JSON.stringify([{ username: 'testuser', emailAlert: true }]) - ); + (getUsersList as jest.Mock).mockResolvedValue([{ username: 'testuser', emailAlert: true }]); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); @@ -71,12 +67,17 @@ describe('Get Email Alert API', () => { user: { name: 'testuser' }, }); - (fs.readFile as jest.Mock).mockRejectedValue({ code: 'ENOENT' }); + (getUsersList as jest.Mock).mockImplementation(() => { + throw new Error(); + }); const { req, res } = createMocks({ method: 'GET' }); await handler(req, res); expect(res._getStatusCode()).toBe(500); - expect(res._getJSONData()).toEqual({ status: 500, message: 'No such file or directory' }); + expect(res._getJSONData()).toEqual({ + status: 500, + message: 'API error, contact the administrator', + }); }); });