From 7e367980fc9aa7c6a1eedc806f2b2bcd48d2927d Mon Sep 17 00:00:00 2001 From: Prakash Choudhary <34452139+prakashchoudhary07@users.noreply.github.com> Date: Wed, 14 Aug 2024 01:54:26 +0530 Subject: [PATCH] Revert "Dev to main sync" --- src/handlers/scheduledEventHandler.ts | 103 +------------- .../handlers/scheduledEventHandler.test.ts | 132 ------------------ src/tests/services/rdsBackendService.test.ts | 8 +- src/tests/utils/apiCaller.test.ts | 78 ----------- src/types/global.types.ts | 18 --- src/utils/apiCaller.ts | 36 ----- src/worker.ts | 41 +----- wrangler.toml | 2 +- 8 files changed, 9 insertions(+), 409 deletions(-) delete mode 100644 src/tests/handlers/scheduledEventHandler.test.ts delete mode 100644 src/tests/utils/apiCaller.test.ts delete mode 100644 src/utils/apiCaller.ts diff --git a/src/handlers/scheduledEventHandler.ts b/src/handlers/scheduledEventHandler.ts index c75c5e0..bbb28ac 100644 --- a/src/handlers/scheduledEventHandler.ts +++ b/src/handlers/scheduledEventHandler.ts @@ -4,14 +4,7 @@ import config from '../config/config'; import { NAMESPACE_NAME } from '../constants'; import { updateUserRoles } from '../services/discordBotServices'; import { getMissedUpdatesUsers } from '../services/rdsBackendService'; -import { - DiscordUserRole, - env, - NicknameUpdateResponseType, - OrphanTasksStatusUpdateResponseType, - UserStatusResponse, -} from '../types/global.types'; -import { apiCaller } from '../utils/apiCaller'; +import { DiscordUserRole, env, NicknameUpdateResponseType } from '../types/global.types'; import { chunks } from '../utils/arrayUtils'; import { generateJwt } from '../utils/generateJwt'; @@ -101,97 +94,3 @@ export const addMissedUpdatesRole = async (env: env) => { console.error('Error while adding missed updates roles'); } }; - -export const syncUsersStatus = async (env: env): Promise => { - await apiCaller(env, 'users/status/update', 'PATCH'); - - try { - const idleUsersData = (await apiCaller(env, 'users/status?aggregate=true', 'GET')) as UserStatusResponse | undefined; - - if (!idleUsersData?.data?.users || idleUsersData.data.users.length === 0) { - console.error('Error: Users data is not in the expected format or no users found'); - return null; - } - - const response = await apiCaller(env, 'users/status/batch', 'PATCH', { - body: JSON.stringify({ users: idleUsersData.data.users }), - }); - - return response; - } catch (error) { - console.error('Error during syncUsersStatus:', error); - return null; - } -}; - -export const syncExternalAccounts = async (env: env) => { - return await apiCaller(env, 'external-accounts/users?action=discord-users-sync', 'POST'); -}; - -export const syncUnverifiedUsers = async (env: env) => { - return await apiCaller(env, 'users', 'POST'); -}; - -export const syncIdleUsers = async (env: env) => { - return await apiCaller(env, 'discord-actions/group-idle', 'PUT'); -}; - -export const syncNickNames = async (env: env) => { - return await apiCaller(env, 'discord-actions/nicknames/sync?dev=true', 'POST'); -}; - -export const syncIdle7dUsers = async (env: env) => { - return await apiCaller(env, 'discord-actions/group-idle-7d', 'PUT'); -}; - -export const syncOnboarding31dPlusUsers = async (env: env) => { - return await apiCaller(env, 'discord-actions/group-onboarding-31d-plus', 'PUT'); -}; - -export async function filterOrphanTasks(env: env) { - const namespace = env[NAMESPACE_NAME] as unknown as KVNamespace; - let lastOrphanTasksFilterationTimestamp: string | null = '0'; // O means it will take the oldest unix timestamp - try { - lastOrphanTasksFilterationTimestamp = await namespace.get('ORPHAN_TASKS_UPDATED_TIME'); - - if (!lastOrphanTasksFilterationTimestamp) { - console.log(`Empty KV ORPHAN_TASKS_UPDATED_TIME: ${lastOrphanTasksFilterationTimestamp}`); - lastOrphanTasksFilterationTimestamp = '0'; // O means it will take the oldest unix timestamp - } - } catch (err) { - console.error(err, 'Error while fetching the timestamp of last orphan tasks filteration'); - throw err; - } - - const url = config(env).RDS_BASE_API_URL; - let token; - try { - token = await generateJwt(env); - } catch (err) { - console.error(`Error while generating JWT token: ${err}`); - throw err; - } - const response = await fetch(`${url}/tasks/orphanTasks`, { - method: 'POST', - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - lastOrphanTasksFilterationTimestamp, - }), - }); - if (!response.ok) { - throw new Error('Error while trying to update status of orphan tasks to backlog'); - } - - const data: OrphanTasksStatusUpdateResponseType = await response.json(); - - try { - await namespace.put('ORPHAN_TASKS_UPDATED_TIME', Date.now().toString()); - } catch (err) { - console.error('Error while trying to update the last orphan tasks filteration timestamp'); - } - - return data; -} diff --git a/src/tests/handlers/scheduledEventHandler.test.ts b/src/tests/handlers/scheduledEventHandler.test.ts deleted file mode 100644 index 1136bda..0000000 --- a/src/tests/handlers/scheduledEventHandler.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { - syncExternalAccounts, - syncIdle7dUsers, - syncIdleUsers, - syncNickNames, - syncOnboarding31dPlusUsers, - syncUnverifiedUsers, - syncUsersStatus, -} from '../../handlers/scheduledEventHandler'; -import { env } from '../../types/global.types'; -import * as apiCallerModule from '../../utils/apiCaller'; - -jest.mock('../../utils/apiCaller', () => ({ - apiCaller: jest.fn(), -})); - -const consoleErrorMock: jest.SpyInstance = jest.spyOn(console, 'error').mockImplementation(); -const apiCallerFunction = apiCallerModule.apiCaller; - -beforeEach(() => { - jest.clearAllMocks(); -}); - -afterAll(() => { - consoleErrorMock.mockRestore(); -}); - -describe('syncUsersStatus', () => { - const mockEnv: env = { - CURRENT_ENVIRONMENT: { - RDS_BASE_API_URL: 'default', - }, - }; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should successfully sync users status', async () => { - (apiCallerFunction as jest.Mock).mockResolvedValueOnce(undefined); - (apiCallerFunction as jest.Mock).mockResolvedValueOnce({ - data: { - users: [{ userId: 'asdoiuahow212' }], - }, - }); - (apiCallerFunction as jest.Mock).mockResolvedValueOnce({ success: true }); - - await syncUsersStatus(mockEnv); - - expect(apiCallerFunction).toHaveBeenCalledWith(mockEnv, 'users/status/update', 'PATCH'); - expect(apiCallerFunction).toHaveBeenCalledWith(mockEnv, 'users/status?aggregate=true', 'GET'); - expect(apiCallerFunction).toHaveBeenCalledWith(mockEnv, 'users/status/batch', 'PATCH', { - body: JSON.stringify({ users: [{ userId: 'asdoiuahow212' }] }), - }); - expect(apiCallerFunction).toHaveBeenCalledTimes(3); - }); - - it('should handle error during users data retrieval', async () => { - (apiCallerFunction as jest.Mock).mockResolvedValueOnce(undefined); - (apiCallerFunction as jest.Mock).mockRejectedValueOnce(new Error('Error fetching users data')); - - const result = await syncUsersStatus(mockEnv); - - expect(result).toBeNull(); - - expect(apiCallerFunction).toHaveBeenCalledWith(mockEnv, 'users/status/update', 'PATCH'); - expect(apiCallerFunction).toHaveBeenCalledWith(mockEnv, 'users/status?aggregate=true', 'GET'); - expect(apiCallerFunction).toHaveBeenCalledTimes(2); - - expect(console.error).toHaveBeenCalledWith('Error during syncUsersStatus:', new Error('Error fetching users data')); - expect(console.error).toHaveBeenCalledTimes(1); - }); - - it('should log an error when no users are found or data is not in the expected format', async () => { - (apiCallerFunction as jest.Mock).mockResolvedValueOnce(undefined); - (apiCallerFunction as jest.Mock).mockResolvedValueOnce({ - data: { - users: [], - }, - }); - - const result = await syncUsersStatus(mockEnv); - - expect(result).toBeNull(); - - expect(apiCallerFunction).toHaveBeenCalledWith(mockEnv, 'users/status/update', 'PATCH'); - expect(apiCallerFunction).toHaveBeenCalledWith(mockEnv, 'users/status?aggregate=true', 'GET'); - expect(apiCallerFunction).toHaveBeenCalledTimes(2); - - expect(console.error).toHaveBeenCalledWith('Error: Users data is not in the expected format or no users found'); - expect(console.error).toHaveBeenCalledTimes(1); - }); -}); - -describe('sync apis', () => { - const mockEnv: env = { - CURRENT_ENVIRONMENT: { - RDS_BASE_API_URL: 'staging', - }, - }; - - const testSyncFunction = async (syncFunction: Function, endpoint: string, method: string) => { - await syncFunction(mockEnv); - - expect(apiCallerFunction).toHaveBeenCalledWith(mockEnv, endpoint, method); - expect(apiCallerFunction).toHaveBeenCalledTimes(1); - }; - - it('should sync unverified users', async () => { - await testSyncFunction(syncUnverifiedUsers, 'users', 'POST'); - }); - - it('should sync idle users', async () => { - await testSyncFunction(syncIdleUsers, 'discord-actions/group-idle', 'PUT'); - }); - - it('should sync external accounts', async () => { - await testSyncFunction(syncExternalAccounts, 'external-accounts/users?action=discord-users-sync', 'POST'); - }); - - it('should sync nicknames', async () => { - await testSyncFunction(syncNickNames, 'discord-actions/nicknames/sync?dev=true', 'POST'); - }); - - it('should sync idle 7d users', async () => { - await testSyncFunction(syncIdle7dUsers, 'discord-actions/group-idle-7d', 'PUT'); - }); - - it('should sync onboarding 31d+ users', async () => { - await testSyncFunction(syncOnboarding31dPlusUsers, 'discord-actions/group-onboarding-31d-plus', 'PUT'); - }); -}); diff --git a/src/tests/services/rdsBackendService.test.ts b/src/tests/services/rdsBackendService.test.ts index 5005f32..994f110 100644 --- a/src/tests/services/rdsBackendService.test.ts +++ b/src/tests/services/rdsBackendService.test.ts @@ -14,7 +14,7 @@ describe('rdsBackendService', () => { }); it('should make a successful API call and return the expected data', async () => { - jest.spyOn(globalThis as any, 'fetch').mockResolvedValueOnce({ + jest.spyOn(global, 'fetch').mockResolvedValueOnce({ ok: true, status: 200, json: jest.fn().mockResolvedValueOnce({ ...missedUpdatesUsersResponse, data: missedUpdatesUsersMock }), @@ -32,7 +32,7 @@ describe('rdsBackendService', () => { expect(result).toEqual({ ...missedUpdatesUsersMock }); }); it('should make a successful API call with cursor', async () => { - jest.spyOn(globalThis as any, 'fetch').mockResolvedValueOnce({ + jest.spyOn(global, 'fetch').mockResolvedValueOnce({ ok: true, status: 200, json: jest.fn().mockResolvedValueOnce({ ...missedUpdatesUsersResponse, data: missedUpdatesUsersMock }), @@ -51,7 +51,7 @@ describe('rdsBackendService', () => { expect(result).toEqual({ ...missedUpdatesUsersMock }); }); it('should throw error when api call fails', async () => { - jest.spyOn(globalThis as any, 'fetch').mockResolvedValueOnce({ + jest.spyOn(global, 'fetch').mockResolvedValueOnce({ ok: false, status: 400, } as unknown as Response); @@ -60,7 +60,7 @@ describe('rdsBackendService', () => { it('should handle unknown errors', async () => { const consoleSpy = jest.spyOn(console, 'error'); - jest.spyOn(globalThis as any, 'fetch').mockRejectedValueOnce(new Error('Error occurred')); + jest.spyOn(global, 'fetch').mockRejectedValueOnce(new Error('Error occurred')); await expect(getMissedUpdatesUsers({}, cursor)).rejects.toThrow('Error occurred'); expect(consoleSpy).toHaveBeenCalledWith('Error occurred while fetching discord user details'); }); diff --git a/src/tests/utils/apiCaller.test.ts b/src/tests/utils/apiCaller.test.ts deleted file mode 100644 index 7ccb1b6..0000000 --- a/src/tests/utils/apiCaller.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { RDS_BASE_DEVELOPMENT_API_URL } from '../../constants/urls'; -import { env } from '../../types/global.types'; -import { apiCaller } from '../../utils/apiCaller'; -import { generateJwt } from '../../utils/generateJwt'; -import * as generateJwtModule from '../../utils/generateJwt'; - -jest.mock('../../utils/generateJwt', () => ({ - generateJwt: jest.fn().mockResolvedValue('mocked-token'), -})); - -describe('apiCaller', () => { - const mockEnv: env = { - CURRENT_ENVIRONMENT: { - RDS_BASE_API_URL: 'default', - }, - }; - - beforeEach(() => { - jest.clearAllMocks(); - (globalThis as any).fetch = jest.fn(() => - Promise.resolve({ - json: () => Promise.resolve({ success: true }), - }), - ); - }); - - it('should make a successful API call', async () => { - const result = await apiCaller(mockEnv, 'users', 'GET'); - expect(generateJwt).toHaveBeenCalledWith(mockEnv); - - expect(result).toEqual({ success: true }); - expect((globalThis as any).fetch).toHaveBeenCalledWith(`${RDS_BASE_DEVELOPMENT_API_URL}/users`, { - method: 'GET', - headers: { - Authorization: 'Bearer mocked-token', - 'Content-Type': 'application/json', - }, - }); - }); - - it('should make a successful POST API call', async () => { - const result = await apiCaller(mockEnv, 'test', 'POST', { - body: JSON.stringify({ data: 'example' }), - }); - expect(generateJwt).toHaveBeenCalledWith(mockEnv); - - expect(result).toEqual({ success: true }); - expect((globalThis as any).fetch).toHaveBeenCalledWith(`${RDS_BASE_DEVELOPMENT_API_URL}/test`, { - method: 'POST', - headers: { - Authorization: 'Bearer mocked-token', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ data: 'example' }), - }); - }); - - it('should log and rethrow error during fetch call failure', async () => { - const mockError = new Error('Network error'); - - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); - - (globalThis as any).fetch = jest.fn().mockRejectedValue(mockError); - - await expect(apiCaller({}, 'someEndpoint', 'GET')).rejects.toThrowError(mockError); - expect(consoleErrorSpy).toHaveBeenCalledWith(`Error during fetch operation: ${mockError}`); - expect(consoleErrorSpy).toHaveBeenCalledTimes(1); - - consoleErrorSpy.mockRestore(); - }); - - it('should handle the case where generateJwt returns undefined and throw an error', async () => { - const generateJwtMock = jest.spyOn(generateJwtModule, 'generateJwt'); - generateJwtMock.mockImplementationOnce(() => Promise.reject(new Error('Generate JWT error'))); - - await expect(apiCaller(mockEnv, 'someEndpoint', 'GET')).rejects.toThrow('Generate JWT error'); - }); -}); diff --git a/src/types/global.types.ts b/src/types/global.types.ts index 30092db..ac46191 100644 --- a/src/types/global.types.ts +++ b/src/types/global.types.ts @@ -19,13 +19,6 @@ export type NicknameUpdateResponseType = { unsuccessfulNicknameUpdates: number; }; }; - -export type OrphanTasksStatusUpdateResponseType = { - message: string; - data: { - orphanTasksUpdatedCount: number; - }; -}; export type DiscordUsersResponse = { message: string; data: DiscordUserIdList; @@ -46,14 +39,3 @@ export type DiscordRoleUpdatedList = { roleid: string; success: boolean; }; -export type UserStatusResponse = { - message: string; - data: { - totalUsers: number; - totalIdleUsers: number; - totalActiveUsers: number; - totalUnprocessedUsers: number; - unprocessedUsers: Array; - users: Array; - }; -}; diff --git a/src/utils/apiCaller.ts b/src/utils/apiCaller.ts deleted file mode 100644 index 79f6474..0000000 --- a/src/utils/apiCaller.ts +++ /dev/null @@ -1,36 +0,0 @@ -import config from '../config/config'; -import { env } from '../types/global.types'; -import { generateJwt } from './generateJwt'; - -export const apiCaller = async ( - env: env, - endpoint: string, - method: string, - options?: Record, -): Promise> => { - const url = config(env).RDS_BASE_API_URL; - let token; - try { - token = await generateJwt(env); - } catch (err) { - console.error(`Error while generating JWT token: ${err}`); - throw err; - } - - const defaultOptions = { - method, - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', - }, - }; - - try { - const response = await fetch(`${url}/${endpoint}`, { ...defaultOptions, ...options }); - return await response.json(); - } catch (error) { - // TODO: Handle these errors: log to newRelic or any other better approach - console.error(`Error during fetch operation: ${error}`); - throw error; - } -}; diff --git a/src/worker.ts b/src/worker.ts index 467a447..60bd74d 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -1,56 +1,21 @@ -import { - addMissedUpdatesRole, - callDiscordNicknameBatchUpdate, - filterOrphanTasks, - syncExternalAccounts, - syncIdle7dUsers, - syncIdleUsers, - syncOnboarding31dPlusUsers, - syncUnverifiedUsers, - syncUsersStatus, -} from './handlers/scheduledEventHandler'; +import { addMissedUpdatesRole, callDiscordNicknameBatchUpdate } from './handlers/scheduledEventHandler'; import { env } from './types/global.types'; const EVERY_6_HOURS = '0 */6 * * *'; const EVERY_11_HOURS = '0 */11 * * *'; -const EVERY_20_MINUTES = '*/20 * * * *'; -const EVERY_30_MINUTES = '*/30 * * * *'; export default { // eslint-disable-next-line no-unused-vars async scheduled(req: ScheduledController, env: env, ctx: ExecutionContext) { switch (req.cron) { case EVERY_6_HOURS: { - await callDiscordNicknameBatchUpdate(env); - await filterOrphanTasks(env); - console.log('Worker for filtering the orphan tasks has completed'); - break; + return await callDiscordNicknameBatchUpdate(env); } case EVERY_11_HOURS: { return await addMissedUpdatesRole(env); } - - case EVERY_20_MINUTES: { - await syncIdleUsers(env); - // await syncNickNames(env); TODO: Enable it once changes from website-backend is merged - await syncIdle7dUsers(env); - await syncOnboarding31dPlusUsers(env); - console.log('Worker for syncing idle users, nicknames, idle 7d users, and onboarding 31d+ users has completed.'); - break; - } - - case EVERY_30_MINUTES: { - await syncUsersStatus(env); - await syncExternalAccounts(env); - await syncUnverifiedUsers(env); - console.log('Worker for syncing user status, external accounts, and unverified users has completed.'); - break; - } - - default: { + default: console.error('Unknown Trigger Value!'); - break; - } } }, // We need to keep all 3 parameters in this format even if they are not used as as cloudflare workers need them to be present So we are disabling eslint rule of no-unused-vars diff --git a/wrangler.toml b/wrangler.toml index dc25df9..2162ea7 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -22,7 +22,7 @@ services = [ ] [triggers] -crons = ["0 */6 * * *","0 */11 * * *","*/20 * * * *","*/30 * * * *" ] +crons = ["0 */6 * * *","0 */11 * * *" ] # # Durable Object binding - For more information: https://developers.cloudflare.com/workers/runtime-apis/durable-objects # [[durable_objects]]