From c0c446ebebe26afe26d1c7ac0b7dcb052b1a0348 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Tue, 6 Jun 2023 23:28:53 +0530 Subject: [PATCH 1/9] RTK query for idle members --- src/app/services/api.ts | 1 + src/app/services/membersApi.ts | 22 ++++++++++++++++ src/constants/url.ts | 1 + src/pages/availability-panel/index.tsx | 35 ++++++++++---------------- 4 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 src/app/services/membersApi.ts diff --git a/src/app/services/api.ts b/src/app/services/api.ts index 9edcb3ad2..0ac58b55a 100644 --- a/src/app/services/api.ts +++ b/src/app/services/api.ts @@ -21,6 +21,7 @@ export const api = createApi({ 'User', 'Tags', 'Levels', + 'Idle_Members' ], /** * This api has endpoints injected in adjacent files, diff --git a/src/app/services/membersApi.ts b/src/app/services/membersApi.ts new file mode 100644 index 000000000..78800d650 --- /dev/null +++ b/src/app/services/membersApi.ts @@ -0,0 +1,22 @@ +import { MEMBERS_IDLE } from '@/constants/url'; +import { api } from './api'; + +type IdleMembersResponse = { idleMemberUserNames: string[] }; +export const membersApi = api.injectEndpoints({ + endpoints: (build) => ({ + getIdleMembers: build.query({ + query: () => MEMBERS_IDLE, + providesTags: ['Idle_Members'], + transformResponse: (response: IdleMembersResponse) => { + const filterMembers = response.idleMemberUserNames.filter( + (username: string) => username + ); + const sortedIdleMembers = filterMembers.sort(); + + return sortedIdleMembers; + }, + }), + }), +}); + +export const { useGetIdleMembersQuery } = membersApi; \ No newline at end of file diff --git a/src/constants/url.ts b/src/constants/url.ts index c04b98f2a..4fd053f5c 100644 --- a/src/constants/url.ts +++ b/src/constants/url.ts @@ -11,6 +11,7 @@ export const LOGIN_URL = `https://github.com/login/oauth/authorize?client_id=23c export const MEMBERS_URL = 'https://members.realdevsquad.com'; export const CHALLENGES_URL = `${BASE_URL}/challenges`; export const USER_SELF = `${BASE_URL}/users/self`; +export const MEMBERS_IDLE = `${BASE_URL}/members/idle`; export const DEFAULT_AVATAR = '/Avatar.png'; export const RDS_LOGO = '/RDSLogo.png'; export const GITHUB_LOGO = '/github-white.png'; diff --git a/src/pages/availability-panel/index.tsx b/src/pages/availability-panel/index.tsx index 054efaa56..9328f1ee2 100644 --- a/src/pages/availability-panel/index.tsx +++ b/src/pages/availability-panel/index.tsx @@ -9,16 +9,23 @@ import updateTasksStatus from '@/helperFunctions/updateTasksStatus'; import { AVAILABLE } from '@/constants/task-status'; import { FEATURE } from '@/constants/task-type'; import { BASE_URL } from '@/constants/url'; +import { useGetIdleMembersQuery } from '@/app/services/membersApi'; const AvailabilityPanel: FC = () => { - const [idleMembersList, setIdleMembersList] = useState([]); const [unAssignedTasks, setUnAssignedTasks] = useState([]); const [error, setError] = useState(false); const [isTaskLoading, setIsTaskLoading] = useState(true); - const [isMemberLoading, setIsMemberLoading] = useState(true); const [refreshData, setRefreshData] = useState(false); + const { + data: idleMembersList, + isError: membersError, + isLoading: isMemberLoading, + refetch: refreshMemberList, + } = useGetIdleMembersQuery(); + useEffect(() => { + const fetchTasks = async () => { try { const url = `${BASE_URL}/tasks`; @@ -36,42 +43,26 @@ const AvailabilityPanel: FC = () => { setIsTaskLoading(false); } }; - const fetchIdleUsers = async () => { - try { - const url = `${process.env.NEXT_PUBLIC_BASE_URL}/members/idle`; - const { requestPromise } = fetch({ url }); - const fetchPromise = await requestPromise; - const { idleMemberUserNames } = fetchPromise.data; - const filterMembers = idleMemberUserNames.filter( - (username: string) => username - ); - const sortedIdleMembers = filterMembers.sort(); - setIdleMembersList(sortedIdleMembers); - setError(false); - } catch (Error) { - setError(true); - } finally { - setIsMemberLoading(false); - } - }; fetchTasks(); - fetchIdleUsers(); }, [refreshData]); + let isErrorOrIsLoading; - if (error) { + if (error || membersError) { isErrorOrIsLoading = ( Something went wrong, please contact admin! ); } else if (isTaskLoading || isMemberLoading) { + isErrorOrIsLoading = ( Loading... ); } const getData = () => { + refreshMemberList(); setRefreshData(!refreshData); }; From 5a0a8158c71c552d498e5e36388683110989cf6e Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Thu, 8 Jun 2023 07:24:34 +0530 Subject: [PATCH 2/9] test case: idle members query --- __tests__/Unit/hooks/membersApi.test.tsx | 44 ++++++++++++++++++++++++ src/app/services/membersApi.ts | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 __tests__/Unit/hooks/membersApi.test.tsx diff --git a/__tests__/Unit/hooks/membersApi.test.tsx b/__tests__/Unit/hooks/membersApi.test.tsx new file mode 100644 index 000000000..52883142b --- /dev/null +++ b/__tests__/Unit/hooks/membersApi.test.tsx @@ -0,0 +1,44 @@ +import { setupServer } from 'msw/node'; +import handlers from '../../../__mocks__/handlers/members.handler.js'; +import React, { PropsWithChildren } from 'react'; +import { act, renderHook } from '@testing-library/react-hooks'; +import { Provider } from 'react-redux'; +import { store } from '@/app/store'; +import { useGetIdleMembersQuery } from '@/app/services/membersApi'; + +const server = setupServer(...handlers); + +beforeAll(() => { + server.listen(); +}); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +function Wrapper({ + children, +}: PropsWithChildren>): JSX.Element { + return {children}; +} + +describe('useGetIdleMembersQuery', () => { + test('returns idle members', async () => { + const { result, waitForNextUpdate } = renderHook( + () => useGetIdleMembersQuery(), + { + wrapper: Wrapper, + } + ); + + const initialResponse = result.current; + expect(initialResponse.data).toBeUndefined(); + expect(initialResponse.isLoading).toBe(true); + + await act(() => waitForNextUpdate()); + + const nextResponse = result.current; + expect(nextResponse.data).not.toBeUndefined(); + expect(nextResponse.isLoading).toBe(false); + expect(nextResponse.isSuccess).toBe(true); + expect(nextResponse.data).toBeDefined(); + }); +}); diff --git a/src/app/services/membersApi.ts b/src/app/services/membersApi.ts index 78800d650..971f228a1 100644 --- a/src/app/services/membersApi.ts +++ b/src/app/services/membersApi.ts @@ -19,4 +19,4 @@ export const membersApi = api.injectEndpoints({ }), }); -export const { useGetIdleMembersQuery } = membersApi; \ No newline at end of file +export const { useGetIdleMembersQuery } = membersApi; From 7d55be3c8776c810b90fe24c296f942fff7749c6 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Thu, 8 Jun 2023 07:57:13 +0530 Subject: [PATCH 3/9] code format fix --- src/app/services/api.ts | 2 +- src/pages/availability-panel/index.tsx | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/app/services/api.ts b/src/app/services/api.ts index 0ac58b55a..519976f0f 100644 --- a/src/app/services/api.ts +++ b/src/app/services/api.ts @@ -21,7 +21,7 @@ export const api = createApi({ 'User', 'Tags', 'Levels', - 'Idle_Members' + 'Idle_Members', ], /** * This api has endpoints injected in adjacent files, diff --git a/src/pages/availability-panel/index.tsx b/src/pages/availability-panel/index.tsx index 9328f1ee2..628ad8003 100644 --- a/src/pages/availability-panel/index.tsx +++ b/src/pages/availability-panel/index.tsx @@ -23,9 +23,7 @@ const AvailabilityPanel: FC = () => { refetch: refreshMemberList, } = useGetIdleMembersQuery(); - useEffect(() => { - const fetchTasks = async () => { try { const url = `${BASE_URL}/tasks`; @@ -46,7 +44,6 @@ const AvailabilityPanel: FC = () => { fetchTasks(); }, [refreshData]); - let isErrorOrIsLoading; if (error || membersError) { isErrorOrIsLoading = ( @@ -55,7 +52,6 @@ const AvailabilityPanel: FC = () => { ); } else if (isTaskLoading || isMemberLoading) { - isErrorOrIsLoading = ( Loading... ); From 9e04da4751a3a6b84921c9580ecb4d31c238dc10 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Thu, 8 Jun 2023 08:10:40 +0530 Subject: [PATCH 4/9] revert changes made to layout --- src/components/Layout/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 902c9e0e7..748ce1eee 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -29,7 +29,7 @@ const Layout: FC = ({ children }) => { // Dev feature toggle const { query } = router; - const dev = true; + const dev = !!query.dev; return (
From 48f025ca1ad70bb69db3ebfeaeeda1e8d77b8921 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Thu, 8 Jun 2023 08:25:47 +0530 Subject: [PATCH 5/9] build issue fix --- src/app/services/membersApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/services/membersApi.ts b/src/app/services/membersApi.ts index 971f228a1..594c2610d 100644 --- a/src/app/services/membersApi.ts +++ b/src/app/services/membersApi.ts @@ -4,7 +4,7 @@ import { api } from './api'; type IdleMembersResponse = { idleMemberUserNames: string[] }; export const membersApi = api.injectEndpoints({ endpoints: (build) => ({ - getIdleMembers: build.query({ + getIdleMembers: build.query({ query: () => MEMBERS_IDLE, providesTags: ['Idle_Members'], transformResponse: (response: IdleMembersResponse) => { From e865a3f8d768013ba96c03c5d3066dc9dc4954b4 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Thu, 8 Jun 2023 09:01:46 +0530 Subject: [PATCH 6/9] build issue fix --- src/pages/availability-panel/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/availability-panel/index.tsx b/src/pages/availability-panel/index.tsx index 628ad8003..39507b32c 100644 --- a/src/pages/availability-panel/index.tsx +++ b/src/pages/availability-panel/index.tsx @@ -17,7 +17,7 @@ const AvailabilityPanel: FC = () => { const [isTaskLoading, setIsTaskLoading] = useState(true); const [refreshData, setRefreshData] = useState(false); const { - data: idleMembersList, + data: idleMembersList = [], isError: membersError, isLoading: isMemberLoading, refetch: refreshMemberList, From 2d1ca25eea140fb7944107651cd5ad03f7c9ddda Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Sat, 10 Jun 2023 12:08:37 +0530 Subject: [PATCH 7/9] remove unnecessary imports --- __tests__/Unit/hooks/membersApi.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/Unit/hooks/membersApi.test.tsx b/__tests__/Unit/hooks/membersApi.test.tsx index 52883142b..eea170bc8 100644 --- a/__tests__/Unit/hooks/membersApi.test.tsx +++ b/__tests__/Unit/hooks/membersApi.test.tsx @@ -1,6 +1,6 @@ import { setupServer } from 'msw/node'; import handlers from '../../../__mocks__/handlers/members.handler.js'; -import React, { PropsWithChildren } from 'react'; +import { PropsWithChildren } from 'react'; import { act, renderHook } from '@testing-library/react-hooks'; import { Provider } from 'react-redux'; import { store } from '@/app/store'; From 2c47a793719fa78a0956510819fb18a8b5c97b5d Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Wed, 14 Jun 2023 06:08:59 +0530 Subject: [PATCH 8/9] renaming variables to improve readability --- src/pages/availability-panel/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/availability-panel/index.tsx b/src/pages/availability-panel/index.tsx index 39507b32c..580f94583 100644 --- a/src/pages/availability-panel/index.tsx +++ b/src/pages/availability-panel/index.tsx @@ -18,8 +18,8 @@ const AvailabilityPanel: FC = () => { const [refreshData, setRefreshData] = useState(false); const { data: idleMembersList = [], - isError: membersError, - isLoading: isMemberLoading, + isError: isIdleMembersError, + isLoading: isIdleMemberLoading, refetch: refreshMemberList, } = useGetIdleMembersQuery(); @@ -45,13 +45,13 @@ const AvailabilityPanel: FC = () => { }, [refreshData]); let isErrorOrIsLoading; - if (error || membersError) { + if (error || isIdleMembersError) { isErrorOrIsLoading = ( Something went wrong, please contact admin! ); - } else if (isTaskLoading || isMemberLoading) { + } else if (isTaskLoading || isIdleMemberLoading) { isErrorOrIsLoading = ( Loading... ); From 95963c9f344bcd93bf9019af98068f1d5634e3fd Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Sun, 18 Jun 2023 09:35:03 +0530 Subject: [PATCH 9/9] Added a test case to check error response --- __mocks__/handlers/members.handler.js | 106 +++++++++++++---------- __tests__/Unit/hooks/membersApi.test.tsx | 31 ++++++- 2 files changed, 90 insertions(+), 47 deletions(-) diff --git a/__mocks__/handlers/members.handler.js b/__mocks__/handlers/members.handler.js index 0e9998f27..9dcd406ad 100644 --- a/__mocks__/handlers/members.handler.js +++ b/__mocks__/handlers/members.handler.js @@ -2,52 +2,66 @@ import { rest } from 'msw'; const URL = process.env.NEXT_PUBLIC_BASE_URL; const membersHandlers = [ - rest.get(`${URL}/members/idle`, (_, res, ctx) => { - return res( - ctx.status(200), - ctx.json({ - message: 'Idle members returned successfully!', - idleMemberUserNames: [ - 'rohan-rajgupta', - 'sumit', - 'swaraj', - 'rohit', - 'tanya', - 'akshay', - 'shubham', - 'devashish', - 'lakshay', - 'rucha', - 'swebert', - 'nikhil', - 'ishika', - 'rajakvk', - 'moses', - 'prem', - 'bhavesh', - 'ankush', - 'prakash', - 'deipayan', - 'mehul', - 'ashwini', - 'amanA', - 'sagar', - 'shankar', - 'aman-saxena', - 'harshith', - 'pranav', - 'ankur', - 'pujarini', - 'sanyogita', - 'pavan', - 'ankita', - 'shashwat', - 'vividh', - 'rahil', - ], - }) - ); - }), + rest.get(`${URL}/members/idle`, (_, res, ctx) => { + return res( + ctx.status(200), + ctx.json({ + message: 'Idle members returned successfully!', + idleMemberUserNames: [ + 'rohan-rajgupta', + 'sumit', + 'swaraj', + 'rohit', + 'tanya', + 'akshay', + 'shubham', + 'devashish', + 'lakshay', + 'rucha', + 'swebert', + 'nikhil', + 'ishika', + 'rajakvk', + 'moses', + 'prem', + 'bhavesh', + 'ankush', + 'prakash', + 'deipayan', + 'mehul', + 'ashwini', + 'amanA', + 'sagar', + 'shankar', + 'aman-saxena', + 'harshith', + 'pranav', + 'ankur', + 'pujarini', + 'sanyogita', + 'pavan', + 'ankita', + 'shashwat', + 'vividh', + 'rahil', + ], + }) + ); + }), ]; +export const failedIdleMembersHandler = rest.get( + `${URL}/members/idle`, + (_, res, ctx) => { + return res( + ctx.status(500), + ctx.json({ + statusCode: 500, + error: 'Internal Server Error', + message: 'An internal server error occurred', + }) + ); + } +); + export default membersHandlers; diff --git a/__tests__/Unit/hooks/membersApi.test.tsx b/__tests__/Unit/hooks/membersApi.test.tsx index eea170bc8..e8a50320d 100644 --- a/__tests__/Unit/hooks/membersApi.test.tsx +++ b/__tests__/Unit/hooks/membersApi.test.tsx @@ -1,5 +1,8 @@ import { setupServer } from 'msw/node'; -import handlers from '../../../__mocks__/handlers/members.handler.js'; +import handlers, { + failedIdleMembersHandler, +} from '../../../__mocks__/handlers/members.handler.js'; + import { PropsWithChildren } from 'react'; import { act, renderHook } from '@testing-library/react-hooks'; import { Provider } from 'react-redux'; @@ -41,4 +44,30 @@ describe('useGetIdleMembersQuery', () => { expect(nextResponse.isSuccess).toBe(true); expect(nextResponse.data).toBeDefined(); }); + + test('checks for error response', async () => { + server.use(failedIdleMembersHandler); + const { result, waitForNextUpdate } = renderHook( + () => useGetIdleMembersQuery(), + { + wrapper: Wrapper, + } + ); + + const initialResponse = result.current; + expect(initialResponse.data).toBeUndefined(); + expect(initialResponse.isLoading).toBe(true); + + await act(() => waitForNextUpdate()); + + const nextResponse = result.current; + expect(nextResponse.isError).toBe(true); + expect(nextResponse.error).toHaveProperty('status', 500); + + expect(nextResponse.error).toHaveProperty('data', { + statusCode: 500, + error: 'Internal Server Error', + message: 'An internal server error occurred', + }); + }); });