/** * Module: API for backend communications. */ import axios, { AxiosError, AxiosRequestConfig } from 'axios'; import { toast } from 'react-toastify'; import { type ErrorData } from '@/components/info/InfoError'; import { ILexemeData, IResolutionData, ITextRequest, ITextResult, IWordFormPlain } from '@/models/language'; import { ICurrentUser, ILibraryItem, ILibraryUpdateData, IPasswordTokenData, IRequestPasswordData, IResetPasswordData as IResetPasswordData, IUserInfo, IUserLoginData, IUserProfile, IUserSignupData, IUserUpdateData, IUserUpdatePassword, IVersionData } from '@/models/library'; import { IConstituentaList, IConstituentaMeta, ICstCreateData, ICstCreatedResponse, ICstMovetoData, ICstRenameData, ICstSubstituteData, ICstTarget, ICstUpdateData, IInlineSynthesisData, IProduceStructureResponse, IRSFormCloneData, IRSFormCreateData, IRSFormData, IRSFormUploadData, IVersionCreatedResponse } 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 postRequestPasswordReset(request: FrontPush) { AxiosPost({ title: 'Request password reset', endpoint: '/users/api/password-reset', request: request }); } export function postValidatePasswordToken(request: FrontPush) { AxiosPost({ title: 'Validate password token', endpoint: '/users/api/password-reset/validate', request: request }); } export function postResetPassword(request: FrontPush) { AxiosPost({ title: 'Reset password', endpoint: '/users/api/password-reset/confirm', 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, version: string, request: FrontPull) { if (!version) { AxiosGet({ title: `RSForm details for id=${target}`, endpoint: `/api/rsforms/${target}/details`, request: request }); } else { AxiosGet({ title: `RSForm details for id=${target} version=${version}`, endpoint: `/api/rsforms/${target}/versions/${version}`, 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 LibraryItem id=${target}`, endpoint: `/api/library/${target}/claim`, request: request }); } export function postSubscribe(target: string, request: FrontAction) { AxiosPost({ title: `Subscribe to LibraryItem 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, version: string, request: FrontPull) { if (!version) { AxiosGet({ title: `RSForm TRS file for id=${target}`, endpoint: `/api/rsforms/${target}/export-trs`, request: request, options: { responseType: 'blob' } }); } else { AxiosGet({ title: `RSForm TRS file for id=${target} version=${version}`, endpoint: `/api/versions/${version}/export-file`, 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-delete-multiple`, 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.target} for schema id=${schema}`, endpoint: `/api/rsforms/${schema}/cst-rename`, request: request }); } export function patchProduceStructure(schema: string, request: FrontExchange) { AxiosPatch({ title: `Producing structure constituenta id=${request.data.target} for schema id=${schema}`, endpoint: `/api/rsforms/${schema}/cst-produce-structure`, request: request }); } export function patchSubstituteConstituents(schema: string, request: FrontExchange) { AxiosPatch({ title: `Substitution for constituents schema id=${schema}`, endpoint: `/api/rsforms/${schema}/cst-substitute`, 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 patchInlineSynthesis(request: FrontExchange) { AxiosPatch({ title: 'Processing inline synthesis', endpoint: `/api/operations/inline-synthesis`, request: request }); } 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 }); } export function postCreateVersion(target: string, request: FrontExchange) { AxiosPost({ title: `Create version for RSForm id=${target}`, endpoint: `/api/rsforms/${target}/versions/create`, request: request }); } export function patchVersion(target: string, request: FrontPush) { AxiosPatch({ title: `Version id=${target}`, endpoint: `/api/versions/${target}`, request: request }); } export function deleteVersion(target: string, request: FrontAction) { AxiosDelete({ title: `Version id=${target}`, endpoint: `/api/versions/${target}`, 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); }); }