/** * Module: API for backend communications. */ import axios, { AxiosError, AxiosRequestConfig } from 'axios'; import { toast } from 'react-toastify'; import { type ErrorData } from '@/components/InfoError'; import { ILexemeData, IResolutionData, ITextRequest, ITextResult, IWordFormPlain } from '@/models/language'; import { ICurrentUser, ILibraryItem, ILibraryUpdateData, IUserInfo, IUserLoginData, IUserProfile, IUserSignupData, IUserUpdateData, IUserUpdatePassword } from '@/models/library'; import { IConstituentaList, IConstituentaMeta, ICstCreateData, ICstCreatedResponse, ICstMovetoData, ICstRenameData, ICstUpdateData, IRSFormCreateData, IRSFormData, IRSFormUploadData} from '@/models/rsform'; import { IExpressionParse, IRSExpression } from '@/models/rslang'; import { buildConstants } from './constants'; const defaultOptions = { xsrfCookieName: 'csrftoken', xsrfHeaderName: 'x-csrftoken', baseURL: `${buildConstants.backend}`, withCredentials: true } const axiosInstance = axios.create(defaultOptions); axiosInstance.interceptors.request.use( (config) => { const token = document.cookie.split('; ') .find((row) => row.startsWith('csrftoken=')) ?.split('=')[1]; if (token) { config.headers['x-csrftoken'] = token; } return config; }); // ================ Data transfer types ================ export type DataCallback = (data: ResponseData) => void; interface IFrontRequest { data?: RequestData onSuccess?: DataCallback onError?: (error: ErrorData) => void setLoading?: (loading: boolean) => void showError?: boolean } export interface FrontPush extends IFrontRequest { data: DataType } export interface FrontPull extends IFrontRequest{ onSuccess: DataCallback } export interface FrontExchange extends IFrontRequest{ data: RequestData onSuccess: DataCallback } export interface FrontAction extends IFrontRequest{} interface IAxiosRequest { endpoint: string request: IFrontRequest title: string options?: AxiosRequestConfig } // ==================== API ==================== export function getAuth(request: FrontPull) { AxiosGet({ title: 'Current user', endpoint: `/users/api/auth`, request: request }); } export function postLogin(request: FrontPush) { AxiosPost({ title: 'Login', endpoint: '/users/api/login', request: request }); } export function postLogout(request: FrontAction) { AxiosPost({ title: 'Logout', endpoint: '/users/api/logout', request: request }); } export function postSignup(request: FrontExchange) { AxiosPost({ title: 'Register user', endpoint: '/users/api/signup', request: request }); } export function getProfile(request: FrontPull) { AxiosGet({ title: 'Current user profile', endpoint: '/users/api/profile', request: request }); } export function patchProfile(request: FrontExchange) { AxiosPatch({ title: 'Current user profile', endpoint: '/users/api/profile', request: request }); } export function patchPassword(request: FrontPush) { AxiosPatch({ title: 'Update Password', endpoint: '/users/api/change-password', request: request }); } export function getActiveUsers(request: FrontPull) { AxiosGet({ title: 'Active users list', endpoint: '/users/api/active-users', request: request }); } export function getLibrary(request: FrontPull) { AxiosGet({ title: 'Available LibraryItems list', endpoint: '/api/library/active', request: request }); } export function getTemplates(request: FrontPull) { AxiosGet({ title: 'Available LibraryItems list', endpoint: '/api/library/templates', request: request }); } export function postNewRSForm(request: FrontExchange) { AxiosPost({ title: 'New RSForm', endpoint: '/api/rsforms/create-detailed', request: request, options: { headers: { 'Content-Type': 'multipart/form-data' } } }); } export function postCloneLibraryItem(target: string, request: FrontExchange) { AxiosPost({ title: 'Clone RSForm', endpoint: `/api/library/${target}/clone`, request: request }); } export function getRSFormDetails(target: string, request: FrontPull) { AxiosGet({ title: `RSForm details for id=${target}`, endpoint: `/api/rsforms/${target}/details`, request: request }); } export function patchLibraryItem(target: string, request: FrontExchange) { AxiosPatch({ title: `LibraryItem id=${target}`, endpoint: `/api/library/${target}`, request: request }); } export function deleteLibraryItem(target: string, request: FrontAction) { AxiosDelete({ title: `LibraryItem id=${target}`, endpoint: `/api/library/${target}`, request: request }); } export function postClaimLibraryItem(target: string, request: FrontPull) { AxiosPost({ title: `Claim on LibrartyItem id=${target}`, endpoint: `/api/library/${target}/claim`, request: request }); } export function postSubscribe(target: string, request: FrontAction) { AxiosPost({ title: `Subscribe to LibrartyItem id=${target}`, endpoint: `/api/library/${target}/subscribe`, request: request }); } export function deleteUnsubscribe(target: string, request: FrontAction) { AxiosDelete({ title: `Unsubscribe from LibraryItem id=${target}`, endpoint: `/api/library/${target}/unsubscribe`, request: request }); } export function getTRSFile(target: string, request: FrontPull) { AxiosGet({ title: `RSForm TRS file for id=${target}`, endpoint: `/api/rsforms/${target}/export-trs`, request: request, options: { responseType: 'blob' } }); } export function postNewConstituenta(schema: string, request: FrontExchange) { AxiosPost({ title: `New Constituenta for RSForm id=${schema}: ${request.data.alias}`, endpoint: `/api/rsforms/${schema}/cst-create`, request: request }); } export function patchDeleteConstituenta(schema: string, request: FrontExchange) { AxiosPatch({ title: `Delete Constituents for RSForm id=${schema}: ${request.data.items.map(item => String(item)).join(' ')}`, endpoint: `/api/rsforms/${schema}/cst-multidelete`, request: request }); } export function patchConstituenta(target: string, request: FrontExchange) { AxiosPatch({ title: `Constituenta id=${target}`, endpoint: `/api/constituents/${target}`, request: request }); } export function patchRenameConstituenta(schema: string, request: FrontExchange) { AxiosPatch({ title: `Renaming constituenta id=${request.data.id} for schema id=${schema}`, endpoint: `/api/rsforms/${schema}/cst-rename`, request: request }); } export function patchMoveConstituenta(schema: string, request: FrontExchange) { AxiosPatch({ title: `Moving Constituents for RSForm id=${schema}: ${JSON.stringify(request.data.items)} to ${request.data.move_to}`, endpoint: `/api/rsforms/${schema}/cst-moveto`, request: request }); } export function postCheckExpression(schema: string, request: FrontExchange) { AxiosPost({ title: `Check expression for RSForm id=${schema}: ${request.data.expression }`, endpoint: `/api/rsforms/${schema}/check`, request: request }); } export function patchResetAliases(target: string, request: FrontPull) { AxiosPatch({ title: `Reset alias for RSForm id=${target}`, endpoint: `/api/rsforms/${target}/reset-aliases`, request: request }); } export function patchUploadTRS(target: string, request: FrontExchange) { AxiosPatch({ title: `Replacing data with trs file for RSForm id=${target}`, endpoint: `/api/rsforms/${target}/load-trs`, request: request, options: { headers: { 'Content-Type': 'multipart/form-data' } } }); } export function postResolveText(schema: string, request: FrontExchange) { AxiosPost({ title: `Resolve text references for RSForm id=${schema}: ${request.data.text}`, endpoint: `/api/rsforms/${schema}/resolve`, request: request }); } export function postInflectText(request: FrontExchange) { AxiosPost({ title: `Inflect text ${request.data.text} to ${request.data.grams}`, endpoint: `/api/cctext/inflect`, request: request }); } export function postParseText(request: FrontExchange) { AxiosPost({ title: `Parse text ${request.data.text}`, endpoint: `/api/cctext/parse`, request: request }); } export function postGenerateLexeme(request: FrontExchange) { AxiosPost({ title: `Parse text ${request.data.text}`, endpoint: `/api/cctext/generate-lexeme`, request: request }); } // ============ Helper functions ============= function AxiosGet({ endpoint, request, title, options }: IAxiosRequest) { console.log(`REQUEST: [[${title}]]`); if (request.setLoading) request.setLoading(true); axiosInstance.get(endpoint, options) .then(response => { if (request.setLoading) request.setLoading(false); if (request.onSuccess) request.onSuccess(response.data); }) .catch((error: Error | AxiosError) => { if (request.setLoading) request.setLoading(false); if (request.showError) toast.error(error.message); if (request.onError) request.onError(error); }); } function AxiosPost( { endpoint, request, title, options }: IAxiosRequest ) { console.log(`POST: [[${title}]]`); if (request.setLoading) request.setLoading(true); axiosInstance.post(endpoint, request.data, options) .then(response => { if (request.setLoading) request.setLoading(false); if (request.onSuccess) request.onSuccess(response.data); }) .catch((error: Error | AxiosError) => { if (request.setLoading) request.setLoading(false); if (request.showError) toast.error(error.message); if (request.onError) request.onError(error); }); } function AxiosDelete( { endpoint, request, title, options }: IAxiosRequest ) { console.log(`DELETE: [[${title}]]`); if (request.setLoading) request.setLoading(true); axiosInstance.delete(endpoint, options) .then(response => { if (request.setLoading) request.setLoading(false); if (request.onSuccess) request.onSuccess(response.data); }) .catch((error: Error | AxiosError) => { if (request.setLoading) request.setLoading(false); if (request.showError) toast.error(error.message); if (request.onError) request.onError(error); }); } function AxiosPatch( { endpoint, request, title, options }: IAxiosRequest ) { console.log(`PATCH: [[${title}]]`); if (request.setLoading) request.setLoading(true); axiosInstance.patch(endpoint, request.data, options) .then(response => { if (request.setLoading) request.setLoading(false); if (request.onSuccess) request.onSuccess(response.data); return response.data; }) .catch((error: Error | AxiosError) => { if (request.setLoading) request.setLoading(false); if (request.showError) toast.error(error.message); if (request.onError) request.onError(error); }); }