From 0c062a848573590bd6ded270790b56aaea2ce251 Mon Sep 17 00:00:00 2001 From: Siddhant Rai Date: Wed, 24 Jul 2024 23:08:42 +0530 Subject: [PATCH] enhancement: implement api client in remaining places --- frontend/src/Navigation.tsx | 92 ++++++++----------- frontend/src/api/endpoints.ts | 10 ++ .../src/api/services/conversationService.ts | 15 +++ frontend/src/api/services/userService.ts | 5 + .../src/modals/ShareConversationModal.tsx | 14 +-- frontend/src/preferences/preferenceApi.ts | 26 ++---- frontend/src/upload/Upload.tsx | 15 +-- 7 files changed, 90 insertions(+), 87 deletions(-) diff --git a/frontend/src/Navigation.tsx b/frontend/src/Navigation.tsx index ebb3f88a..22ab5017 100644 --- a/frontend/src/Navigation.tsx +++ b/frontend/src/Navigation.tsx @@ -1,46 +1,48 @@ import { useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; import { NavLink, useNavigate } from 'react-router-dom'; + +import conversationService from './api/services/conversationService'; +import userService from './api/services/userService'; +import Add from './assets/add.svg'; import DocsGPT3 from './assets/cute_docsgpt3.svg'; import Discord from './assets/discord.svg'; import Expand from './assets/expand.svg'; import Github from './assets/github.svg'; -import Hamburger from './assets/hamburger.svg'; import HamburgerDark from './assets/hamburger-dark.svg'; +import Hamburger from './assets/hamburger.svg'; import Info from './assets/info.svg'; import SettingGear from './assets/settingGear.svg'; import Twitter from './assets/TwitterX.svg'; -import Add from './assets/add.svg'; import UploadIcon from './assets/upload.svg'; -import { ActiveState } from './models/misc'; -import APIKeyModal from './preferences/APIKeyModal'; -import DeleteConvModal from './modals/DeleteConvModal'; - -import { - selectApiKeyStatus, - selectSelectedDocs, - selectSelectedDocsStatus, - selectSourceDocs, - setSelectedDocs, - selectConversations, - setConversations, - selectConversationId, - selectModalStateDeleteConv, - setModalStateDeleteConv, - setSourceDocs, -} from './preferences/preferenceSlice'; +import SourceDropdown from './components/SourceDropdown'; import { setConversation, updateConversationId, } from './conversation/conversationSlice'; -import { useMediaQuery, useOutsideAlerter } from './hooks'; -import Upload from './upload/Upload'; -import { Doc, getConversations, getDocs } from './preferences/preferenceApi'; -import SelectDocsModal from './preferences/SelectDocsModal'; import ConversationTile from './conversation/ConversationTile'; -import { useDarkTheme } from './hooks'; -import SourceDropdown from './components/SourceDropdown'; -import { useTranslation } from 'react-i18next'; +import { useDarkTheme, useMediaQuery, useOutsideAlerter } from './hooks'; +import DeleteConvModal from './modals/DeleteConvModal'; +import { ActiveState } from './models/misc'; +import APIKeyModal from './preferences/APIKeyModal'; +import { Doc, getConversations, getDocs } from './preferences/preferenceApi'; +import { + selectApiKeyStatus, + selectConversationId, + selectConversations, + selectModalStateDeleteConv, + selectSelectedDocs, + selectSelectedDocsStatus, + selectSourceDocs, + setConversations, + setModalStateDeleteConv, + setSelectedDocs, + setSourceDocs, +} from './preferences/preferenceSlice'; +import SelectDocsModal from './preferences/SelectDocsModal'; +import Upload from './upload/Upload'; + interface NavigationProps { navOpen: boolean; setNavOpen: React.Dispatch>; @@ -85,7 +87,6 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { useState('INACTIVE'); const navRef = useRef(null); - const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; const navigate = useNavigate(); @@ -106,9 +107,8 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { } const handleDeleteAllConversations = () => { - fetch(`${apiHost}/api/delete_all_conversations`, { - method: 'POST', - }) + conversationService + .deleteAll({}) .then(() => { fetchConversations(); }) @@ -116,9 +116,8 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { }; const handleDeleteConversation = (id: string) => { - fetch(`${apiHost}/api/delete_conversation?id=${id}`, { - method: 'POST', - }) + conversationService + .delete(id, {}) .then(() => { fetchConversations(); }) @@ -128,17 +127,9 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { const handleDeleteClick = (doc: Doc) => { const docPath = `indexes/local/${doc.name}`; - fetch(`${apiHost}/api/delete_old?path=${docPath}`, { - method: 'GET', - }) + userService + .deletePath(docPath) .then(() => { - // remove the image element from the DOM - // const imageElement = document.querySelector( - // `#img-${index}`, - // ) as HTMLElement; - // const parentElement = imageElement.parentNode as HTMLElement; - // parentElement.parentNode?.removeChild(parentElement); - return getDocs(); }) .then((updatedDocs) => { @@ -153,10 +144,8 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { }; const handleConversationClick = (index: string) => { - // fetch the conversation from the server and setConversation in the store - fetch(`${apiHost}/api/get_single_conversation?id=${index}`, { - method: 'GET', - }) + conversationService + .getConversation(index) .then((response) => response.json()) .then((data) => { navigate('/'); @@ -173,13 +162,8 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { name: string; id: string; }) { - await fetch(`${apiHost}/api/update_conversation_name`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(updatedConversation), - }) + await conversationService + .update(updatedConversation) .then((response) => response.json()) .then((data) => { if (data) { diff --git a/frontend/src/api/endpoints.ts b/frontend/src/api/endpoints.ts index 790fcb12..af2fb920 100644 --- a/frontend/src/api/endpoints.ts +++ b/frontend/src/api/endpoints.ts @@ -1,5 +1,7 @@ const endpoints = { USER: { + DOCS: '/api/combine', + DOCS_CHECK: '/api/docs_check', API_KEYS: '/api/get_api_keys', CREATE_API_KEY: '/api/create_api_key', DELETE_API_KEY: '/api/delete_api_key', @@ -9,14 +11,22 @@ const endpoints = { UPDATE_PROMPT: '/api/update_prompt', SINGLE_PROMPT: (id: string) => `/api/get_single_prompt?id=${id}`, DELETE_PATH: (docPath: string) => `/api/delete_old?path=${docPath}`, + TASK_STATUS: (task_id: string) => `/api/task_status?task_id=${task_id}`, }, CONVERSATION: { ANSWER: '/api/answer', ANSWER_STREAMING: '/stream', SEARCH: '/api/search', FEEDBACK: '/api/feedback', + CONVERSATION: (id: string) => `/api/get_single_conversation?id=${id}`, + CONVERSATIONS: '/api/get_conversations', + SHARE_CONVERSATION: (isPromptable: boolean) => + `/api/share?isPromptable=${isPromptable}`, SHARED_CONVERSATION: (identifier: string) => `/api/shared_conversation/${identifier}`, + DELETE: (id: string) => `/api/delete_conversation?id=${id}`, + DELETE_ALL: '/api/delete_all_conversations', + UPDATE: '/api/update_conversation_name', }, }; diff --git a/frontend/src/api/services/conversationService.ts b/frontend/src/api/services/conversationService.ts index f41a5d31..9e31df84 100644 --- a/frontend/src/api/services/conversationService.ts +++ b/frontend/src/api/services/conversationService.ts @@ -10,8 +10,23 @@ const conversationService = { apiClient.post(endpoints.CONVERSATION.SEARCH, data), feedback: (data: any): Promise => apiClient.post(endpoints.CONVERSATION.FEEDBACK, data), + getConversation: (id: string): Promise => + apiClient.get(endpoints.CONVERSATION.CONVERSATION(id)), + getConversations: (): Promise => + apiClient.get(endpoints.CONVERSATION.CONVERSATIONS), + shareConversation: (isPromptable: boolean, data: any): Promise => + apiClient.post( + endpoints.CONVERSATION.SHARE_CONVERSATION(isPromptable), + data, + ), getSharedConversation: (identifier: string): Promise => apiClient.get(endpoints.CONVERSATION.SHARED_CONVERSATION(identifier)), + delete: (id: string, data: any): Promise => + apiClient.post(endpoints.CONVERSATION.DELETE(id), data), + deleteAll: (data: any): Promise => + apiClient.post(endpoints.CONVERSATION.DELETE_ALL, data), + update: (data: any): Promise => + apiClient.post(endpoints.CONVERSATION.UPDATE, data), }; export default conversationService; diff --git a/frontend/src/api/services/userService.ts b/frontend/src/api/services/userService.ts index 7e65e94e..193fe6ad 100644 --- a/frontend/src/api/services/userService.ts +++ b/frontend/src/api/services/userService.ts @@ -2,6 +2,9 @@ import apiClient from '../client'; import endpoints from '../endpoints'; const userService = { + getDocs: (): Promise => apiClient.get(endpoints.USER.DOCS), + checkDocs: (data: any): Promise => + apiClient.post(endpoints.USER.DOCS_CHECK, data), getAPIKeys: (): Promise => apiClient.get(endpoints.USER.API_KEYS), createAPIKey: (data: any): Promise => apiClient.post(endpoints.USER.CREATE_API_KEY, data), @@ -18,6 +21,8 @@ const userService = { apiClient.get(endpoints.USER.SINGLE_PROMPT(id)), deletePath: (docPath: string): Promise => apiClient.get(endpoints.USER.DELETE_PATH(docPath)), + getTaskStatus: (task_id: string): Promise => + apiClient.get(endpoints.USER.TASK_STATUS(task_id)), }; export default userService; diff --git a/frontend/src/modals/ShareConversationModal.tsx b/frontend/src/modals/ShareConversationModal.tsx index 37da934d..a43e4326 100644 --- a/frontend/src/modals/ShareConversationModal.tsx +++ b/frontend/src/modals/ShareConversationModal.tsx @@ -1,8 +1,9 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import Spinner from '../assets/spinner.svg'; + +import conversationService from '../api/services/conversationService'; import Exit from '../assets/exit.svg'; -const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; +import Spinner from '../assets/spinner.svg'; export const ShareConversationModal = ({ close, @@ -25,13 +26,8 @@ export const ShareConversationModal = ({ isPromptable = false, ) => { setStatus('loading'); - fetch(`${apiHost}/api/share?isPromptable=${isPromptable}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ conversation_id: conversationId }), - }) + conversationService + .shareConversation(isPromptable, { conversation_id: conversationId }) .then((res) => { console.log(res.status); return res.json(); diff --git a/frontend/src/preferences/preferenceApi.ts b/frontend/src/preferences/preferenceApi.ts index 81fd3131..29a41645 100644 --- a/frontend/src/preferences/preferenceApi.ts +++ b/frontend/src/preferences/preferenceApi.ts @@ -1,3 +1,6 @@ +import conversationService from '../api/services/conversationService'; +import userService from '../api/services/userService'; + // not all properties in Doc are going to be present. Make some optional export type Doc = { location: string; @@ -14,10 +17,7 @@ export type Doc = { //Fetches all JSON objects from the source. We only use the objects with the "model" property in SelectDocsModal.tsx. Hopefully can clean up the source file later. export async function getDocs(): Promise { try { - const apiHost = - import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; - - const response = await fetch(apiHost + '/api/combine'); + const response = await userService.getDocs(); const data = await response.json(); const docs: Doc[] = []; @@ -37,10 +37,7 @@ export async function getConversations(): Promise< { name: string; id: string }[] | null > { try { - const apiHost = - import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; - - const response = await fetch(apiHost + '/api/get_conversations'); + const response = await conversationService.getConversations(); const data = await response.json(); const conversations: { name: string; id: string }[] = []; @@ -93,14 +90,9 @@ export function setLocalRecentDocs(doc: Doc): void { docPath = doc.language + '/' + namePath + '/' + doc.version + '/' + doc.model + '/'; } - const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; - fetch(apiHost + '/api/docs_check', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ + userService + .checkDocs({ docs: docPath, - }), - }).then((response) => response.json()); + }) + .then((response) => response.json()); } diff --git a/frontend/src/upload/Upload.tsx b/frontend/src/upload/Upload.tsx index a2c23639..3bb3e7ae 100644 --- a/frontend/src/upload/Upload.tsx +++ b/frontend/src/upload/Upload.tsx @@ -1,13 +1,14 @@ -import React, { useRef } from 'react'; -import { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useDropzone } from 'react-dropzone'; +import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; + +import userService from '../api/services/userService'; +import Dropdown from '../components/Dropdown'; +import Input from '../components/Input'; import { ActiveState } from '../models/misc'; import { getDocs } from '../preferences/preferenceApi'; import { setSelectedDocs, setSourceDocs } from '../preferences/preferenceSlice'; -import Dropdown from '../components/Dropdown'; -import { useTranslation } from 'react-i18next'; -import Input from '../components/Input'; function Upload({ modalState, @@ -111,8 +112,8 @@ function Upload({ if ((progress?.percentage ?? 0) < 100) { timeoutID = setTimeout(() => { - const apiHost = import.meta.env.VITE_API_HOST; - fetch(`${apiHost}/api/task_status?task_id=${progress?.taskId}`) + userService + .getTaskStatus(progress?.taskId as string) .then((data) => data.json()) .then((data) => { if (data.status == 'SUCCESS') {