UNPKG

drf-react-by-schema

Version:

Components and Tools for building a React App having Django Rest Framework (DRF) as server

851 lines (850 loc) 31.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAllModels = exports.getGenericModel = exports.getGenericModelList = exports.signUp = exports.getSignUpOptions = exports.isLoggedIn = exports.setAuthToken = exports.hasJWT = exports.clearJWT = exports.loginByPayload = exports.getAutoComplete = exports.addExistingRelatedModel = exports.updateDataBySchema = exports.createOrUpdateData = exports.deleteData = exports.createData = exports.partialUpdateData = exports.updateData = exports.getRawData = void 0; const axios_1 = __importDefault(require("axios")); const dayjs_1 = __importDefault(require("dayjs")); const utils_1 = require("./utils"); const expiredTokenMessage = 'Token expired! System must login again'; const noServerEndPointMessage = 'There is not API definition (serverEndPoint)!'; const unknownError = 'Unknown error!'; const getOptions = (path, serverEndPoint) => __awaiter(void 0, void 0, void 0, function* () { var _a; if (!serverEndPoint) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const url = `${serverEndPoint.api}/${path}`; try { const { data } = yield axios_1.default.options(url); return data; } catch (e) { if (axios_1.default.isAxiosError(e)) { const err = e; const message = `Error fetching options from ${url}`; if (((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) === 401) { const isRefreshed = yield refreshToken(serverEndPoint); if (!isRefreshed) { console.log(expiredTokenMessage); return { message: expiredTokenMessage, isAxiosError: true, }; } try { const { data } = yield axios_1.default.options(url); return data; } catch (e) { console.log(message, e); return { message, isAxiosError: true, }; } } console.log(message, e); return { message, isAxiosError: true, }; } return { message: unknownError, isAxiosError: true, }; } }); const getSchema = (path, serverEndPoint) => __awaiter(void 0, void 0, void 0, function* () { const options = yield getOptions(path, serverEndPoint); if ('isAxiosError' in options) { return false; } // Special default value of "currentUser": let usuaria = false; const postActions = options.actions.POST; for (const [key, field] of Object.entries(postActions)) { if (field.model_default === 'currentUser') { if (!usuaria) { usuaria = yield (0, exports.isLoggedIn)(serverEndPoint); } if (usuaria) { options.actions.POST[key].model_default = { id: usuaria.id, label: usuaria.first_name, }; } } if (field.model_default && field.type === 'choice') { if (field.choices) { const modelDefault = field.choices.filter((choice) => choice.value === field.model_default); options.actions.POST[key].model_default = modelDefault.length === 1 ? modelDefault[0] : undefined; } } } const modelOptions = { name: options.name, description: options.description, verbose_name: options.verbose_name, verbose_name_plural: options.verbose_name_plural, }; return { schema: options.actions.POST, modelOptions }; }); const getRawData = (_a) => __awaiter(void 0, [_a], void 0, function* ({ path, serverEndPoint, }) { var _b; if (!serverEndPoint) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const url = `${serverEndPoint.api}/${path}`; try { const { data } = yield axios_1.default.get(url); return data; } catch (e) { const message = `Error fetching data from ${url}`; if (axios_1.default.isAxiosError(e)) { const err = e; if (((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) === 401) { const isRefreshed = yield refreshToken(serverEndPoint); if (!isRefreshed) { console.log(expiredTokenMessage); return { message: expiredTokenMessage, isAxiosError: true, }; } try { const { data } = yield axios_1.default.get(url); return data; } catch (e) { const message = `Error fetching data from ${url}`; console.log(message, e); return { message, isAxiosError: true, }; } } } console.log(message, e); return { message, isAxiosError: true, }; } }); exports.getRawData = getRawData; const getData = (_a) => __awaiter(void 0, [_a], void 0, function* ({ path, serverEndPoint, route = 'api', }) { var _b; if (!serverEndPoint) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const url = `${serverEndPoint[route]}/${path}`; try { const { data } = yield axios_1.default.get(url); return data; } catch (e) { if (axios_1.default.isAxiosError(e)) { const err = e; if (((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) === 401) { const isRefreshed = yield refreshToken(serverEndPoint); if (!isRefreshed) { console.log(expiredTokenMessage); return { message: expiredTokenMessage, isAxiosError: true, }; } try { const { data } = yield axios_1.default.get(url); return data; } catch (e) { console.log(expiredTokenMessage, e); return { message: expiredTokenMessage, isAxiosError: true, }; } } } const message = `Error fetching data from ${url}`; console.log(message, e); return { message, isAxiosError: true, }; } }); /** * * @param options - params * @returns updatedData when succesfully updated, axiosError otherwise */ const updateData = (_a) => __awaiter(void 0, [_a], void 0, function* ({ path, serverEndPoint, data, id, contentType = 'application/json', }) { var _b, _c, _d; if (!serverEndPoint) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const url = `${serverEndPoint.api}/${path}/${id}/`; const config = { headers: { 'Content-Type': contentType } }; try { const { data: updatedData } = yield axios_1.default.put(url, data, config); return updatedData; } catch (e) { if (axios_1.default.isAxiosError(e)) { const err = e; const message = `Error updating data at ${url}`; if (((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) === 401) { const isRefreshed = yield refreshToken(serverEndPoint); if (!isRefreshed) { console.log(expiredTokenMessage); return { message: expiredTokenMessage, isAxiosError: true, }; } try { const { data: updatedData } = yield axios_1.default.put(url, data, config); return updatedData; } catch (e) { console.log(message, data, (_c = err.response) === null || _c === void 0 ? void 0 : _c.data, e); return { message, isAxiosError: true }; } } console.log(message, data, (_d = err.response) === null || _d === void 0 ? void 0 : _d.data); return { message, isAxiosError: true }; } } return { message: unknownError, isAxiosError: true }; }); exports.updateData = updateData; const partialUpdateData = (_a) => __awaiter(void 0, [_a], void 0, function* ({ path, serverEndPoint, data, id }) { var _b, _c; if (!serverEndPoint) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const url = `${serverEndPoint.api}/${path}/${id}/`; try { const { data: updatedData } = yield axios_1.default.patch(url, data); return updatedData; } catch (e) { if (axios_1.default.isAxiosError(e)) { const err = e; const message = `Error partial updating data at ${url}`; if (((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) === 401) { const isRefreshed = yield refreshToken(serverEndPoint); if (!isRefreshed) { console.log(expiredTokenMessage); return { message: expiredTokenMessage, isAxiosError: true, }; } try { const { data: updatedData } = yield axios_1.default.patch(url, data); return updatedData; } catch (e) { console.log(message, data, e); return { message: err.response ? err.response.data : message, isAxiosError: true, }; } } console.log(message, data, (_c = err.response) === null || _c === void 0 ? void 0 : _c.data); return { message: err.response ? err.response.data : message, isAxiosError: true, }; } } return { message: unknownError, isAxiosError: true }; }); exports.partialUpdateData = partialUpdateData; const createData = (_a) => __awaiter(void 0, [_a], void 0, function* ({ path, serverEndPoint, data, contentType = 'application/json', }) { var _b, _c; if (!serverEndPoint) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const url = `${serverEndPoint.api}/${path}/`; const config = { headers: { 'Content-Type': contentType } }; try { const { data: createdData } = yield axios_1.default.post(url, data, config); return createdData; } catch (e) { if (axios_1.default.isAxiosError(e)) { const err = e; const message = `Error creating data at ${url}`; if (((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) === 401) { const isRefreshed = yield refreshToken(serverEndPoint); if (!isRefreshed) { console.log(expiredTokenMessage); return { message: expiredTokenMessage, isAxiosError: true, }; } try { const { data: createdData } = yield axios_1.default.post(url, data); return createdData; } catch (e) { console.log(message, data, e); return { message: err.response ? err.response.data : message, isAxiosError: true, }; } } console.log(message, data, (_c = err.response) === null || _c === void 0 ? void 0 : _c.data); return { message: err.response ? err.response.data : message, isAxiosError: true, }; } } return { message: unknownError, isAxiosError: true, }; }); exports.createData = createData; const deleteData = (path, serverEndPoint, id) => __awaiter(void 0, void 0, void 0, function* () { var _a; if (!serverEndPoint) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const url = `${serverEndPoint.api}/${path}/${id}`; try { yield axios_1.default.delete(url); return true; } catch (e) { if (axios_1.default.isAxiosError(e)) { const err = e; const message = `Error deleting data from ${url}`; if (((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) === 401) { const isRefreshed = yield refreshToken(serverEndPoint); if (!isRefreshed) { console.log(expiredTokenMessage); return { message: expiredTokenMessage, isAxiosError: true, }; } try { yield axios_1.default.delete(url); return true; } catch (e) { console.log(message, e); return { message: err.response ? err.response.data : message, isAxiosError: true, }; } } console.log(message, e); return { message: err.response ? err.response.data : message, isAxiosError: true, }; } } return { message: unknownError, isAxiosError: true, }; }); exports.deleteData = deleteData; const createOrUpdateData = (_a) => __awaiter(void 0, [_a], void 0, function* ({ path, serverEndPoint, data, id, contentType = 'application/json', }) { if ((0, utils_1.isTmpId)(id) || !id) { const responseCreate = yield (0, exports.createData)({ path, serverEndPoint, data, contentType }); return responseCreate; } const responseUpdate = yield (0, exports.updateData)({ path, serverEndPoint, data, id, contentType }); return responseUpdate; }); exports.createOrUpdateData = createOrUpdateData; const prepareDataBySchemaRecursive = ({ data, schema }) => { let requiresMultipart = false; const dbData = {}; for (const [key, field] of Object.entries(schema)) { if (!(key in data) || (key === 'id' && (0, utils_1.isTmpId)(data[key]))) { continue; } if (typeof data[key] !== 'boolean' && !data[key]) { dbData[key] = (0, utils_1.emptyByType)(field, true); continue; } // Check for file upload fields if (['file upload', 'image upload'].includes(field.type)) { requiresMultipart = true; if ('file' in data[key]) { dbData[key] = { file: data[key].file }; } continue; } // OneToMany or ManyToMany relations: if (field.type === 'field' && field.child) { const dataArr = data[key]; dbData[key] = dataArr.map((row) => { var _a; return ((_a = field.child) === null || _a === void 0 ? void 0 : _a.children) ? prepareDataBySchemaRecursive({ data: row, schema: field.child.children, }).data : row; }); continue; } // Nested Object: if (field.type === 'nested object' && field.children && typeof data[key] === 'object') { dbData[key] = (0, utils_1.isTmpId)(data[key].id) ? prepareDataBySchemaRecursive({ data: data[key], schema: field.children, }).data : data[key].id; continue; } // Choices: if (field.type === 'choice') { dbData[key] = data[key].value; continue; } // Date: if (field.type === 'date') { dbData[key] = (0, dayjs_1.default)(data[key]).format('YYYY-MM-DD'); continue; } // DateTime: if (field.type === 'datetime') { dbData[key] = (0, dayjs_1.default)(data[key]).format('YYYY-MM-DDTHH:mm'); continue; } // Default: dbData[key] = data[key]; } return { data: dbData, requiresMultipart, }; }; const prepareDataBySchema = ({ data, schema }) => { const { data: preparedData, requiresMultipart } = prepareDataBySchemaRecursive({ data, schema }); return { data: requiresMultipart ? (0, utils_1.convertToFormData)(preparedData) : preparedData, contentType: requiresMultipart ? 'multipart/form-data' : 'application/json', }; }; const updateDataBySchema = (_a) => __awaiter(void 0, [_a], void 0, function* ({ model, modelObjectId, serverEndPoint, data, schema, path = null, }) { const { data: dbData, contentType } = prepareDataBySchema({ data, schema }); if (!path) { path = model; } const response = yield (0, exports.createOrUpdateData)({ path, serverEndPoint, data: dbData, id: modelObjectId, contentType, }); return response; }); exports.updateDataBySchema = updateDataBySchema; const addExistingRelatedModel = (_a) => __awaiter(void 0, [_a], void 0, function* ({ model, serverEndPoint, id, data, }) { const response = yield (0, exports.partialUpdateData)({ path: model, serverEndPoint, data, id }); return response; }); exports.addExistingRelatedModel = addExistingRelatedModel; const getDataGridColumns = (schema, columnFields, hiddenFields, creatableFields, disabledFields) => { if (!schema) { return false; } const finalColumnFields = !columnFields || columnFields.length === 0 ? Object.keys(schema) : columnFields.filter((field) => { return field in schema; }); return finalColumnFields.map((field) => { const column = { field, headerName: schema[field].label, hide: hiddenFields && hiddenFields.includes(field), creatable: creatableFields && creatableFields.includes(field) ? true : 'creatable' in schema[field] ? schema[field].creatable : false, disabled: !!disabledFields && disabledFields.includes(field), uiRequired: schema[field].ui_required || false, width: 100, }; return column; }); }; const getAutoComplete = (_a) => __awaiter(void 0, [_a], void 0, function* ({ model, serverEndPoint, }) { const data = yield getData({ path: `${model}/`, serverEndPoint, route: 'autocomplete' }); if ('isAxiosError' in data) { return false; } return data; }); exports.getAutoComplete = getAutoComplete; const loginByPayload = (payload, serverEndPoint) => __awaiter(void 0, void 0, void 0, function* () { if (!serverEndPoint) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const url = serverEndPoint.getToken; if (!url) { console.log('Erro no loginByPayload: faltou a configuração de url getToken!'); return false; } try { const { data } = yield axios_1.default.post(url, payload); localStorage.setItem('refreshToken', data.refresh); (0, exports.setAuthToken)(data.access); return true; } catch (e) { console.log('Erro no loginByPayload!', e); (0, exports.setAuthToken)(null); return false; } }); exports.loginByPayload = loginByPayload; const clearJWT = () => { localStorage.removeItem('token'); return false; }; exports.clearJWT = clearJWT; const hasJWT = () => { const token = localStorage.getItem('token'); if (token) { return true; } return false; }; exports.hasJWT = hasJWT; const setAuthToken = (token) => { if (token) { localStorage.setItem('token', token); axios_1.default.defaults.headers.common.Authorization = `Bearer ${token}`; return; } localStorage.removeItem('token'); localStorage.removeItem('refreshToken'); delete axios_1.default.defaults.headers.common.Authorization; }; exports.setAuthToken = setAuthToken; const refreshToken = (serverEndPoint) => __awaiter(void 0, void 0, void 0, function* () { if (!serverEndPoint) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const refreshToken = localStorage.getItem('refreshToken'); (0, exports.setAuthToken)(null); if (!refreshToken) { return false; } try { const { data } = yield axios_1.default.post(`${serverEndPoint.refreshToken}`, { refresh: refreshToken, }); (0, exports.setAuthToken)(data.access); return true; } catch (e) { console.log('Failed refreshing token', e); return false; } }); const isLoggedIn = (serverEndPoint) => __awaiter(void 0, void 0, void 0, function* () { const token = localStorage.getItem('token'); (0, exports.setAuthToken)(token); if (!token) { return false; } const usuaria = yield getData({ path: 'minhaconta/', serverEndPoint }); if ('isAxiosError' in usuaria) { console.log('Erro ao recuperar dados de usuária!'); return false; } return usuaria; }); exports.isLoggedIn = isLoggedIn; const getSignUpOptions = (serverEndPoint) => __awaiter(void 0, void 0, void 0, function* () { if (!serverEndPoint || !serverEndPoint.signUp) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const url = serverEndPoint.signUp; try { const { data } = yield axios_1.default.options(url); return data.action.POST; } catch (e) { const message = `Error fetching options from ${url}`; console.log(message, e); return { message, isAxiosError: true, }; } }); exports.getSignUpOptions = getSignUpOptions; const signUp = (data, serverEndPoint) => __awaiter(void 0, void 0, void 0, function* () { var _a; if (!serverEndPoint || !serverEndPoint.signUp) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const url = serverEndPoint.signUp; try { const { data: responseData } = yield axios_1.default.post(url, data); return responseData; } catch (e) { if (axios_1.default.isAxiosError(e)) { const err = e; const message = `Error trying signUp at ${url}`; if (((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) === 401) { const isRefreshed = yield refreshToken(serverEndPoint); if (!isRefreshed) { console.log(expiredTokenMessage); return { message: expiredTokenMessage, isAxiosError: true, }; } try { const { data: responseData } = yield axios_1.default.post(url, data); return responseData; } catch (e) { console.log(message, data, e); return { message, isAxiosError: true, }; } } console.log(message, data, e); return { message, isAxiosError: true, }; } } console.log(unknownError, data); return { message: unknownError, isAxiosError: true, }; }); exports.signUp = signUp; const getGenericModelList = (_a) => __awaiter(void 0, [_a], void 0, function* ({ model, serverEndPoint, id = '', relatedModel = '', relatedModelId = '', columnFields, hiddenFields = ['id'], creatableFields = [], disabledFields = [], isInBatches = false, loadedSchema, loadedModelOptions, page, filter, queryParams = [], sort, sumRows, }) { const newQueryParams = [...queryParams]; let path = `${model}/${id}`; let schemaPath = `${model}/`; let schema = loadedSchema; let modelOptions = loadedModelOptions; let columns = []; if (!(0, utils_1.isTmpId)(id) && relatedModel) { path += `/${relatedModel}/`; if (relatedModelId) { path += `${relatedModelId}/`; } schemaPath += `${id}/${relatedModel}/`; } // SERVER-SIDE TOTALS (sumRows): if (sumRows) { const sumRowsParams = sumRows.rows.map((row) => row.field).join(','); newQueryParams.push(`sum_rows=${sumRowsParams}`); } // SERVER-SIDE FILTERING: if (filter) { if (filter.quickFilterValues && filter.quickFilterValues.length > 0 && filter.quickFilterValues[0]) { newQueryParams.push(`search=${filter.quickFilterValues[0]}`); } for (const item of filter.items) { if (!item.operatorValue) { continue; } const queryParam = item.value ? `columnField=${item.columnField}&operatorValue=${item.operatorValue}&value=${item.value}` : `columnField=${item.columnField}&operatorValue=${item.operatorValue}`; newQueryParams.push(queryParam); } } // SERVER-SIDE SORTING: if (sort) { const sortParams = []; for (const item of sort) { sortParams.push(item.sort === 'desc' ? `-${item.field}` : item.field); } newQueryParams.push(`ordering=${sortParams.join(',')}`); } // SERVER-SIDE PAGINATION: if (page) { newQueryParams.push(`page=${page + 1}`); } // Only get schema and columns if not in batches or in first batch: if (!schema) { const options = yield getSchema(schemaPath, serverEndPoint); if (!options) { return false; } schema = options.schema; modelOptions = options.modelOptions; columns = getDataGridColumns(schema, columnFields, hiddenFields, creatableFields, disabledFields); if (!columns) { return false; } } let rowCount = 0; let sumRowsTotals = null; let data = []; if (!id || (id && !relatedModelId)) { if (isInBatches) { newQueryParams.push(loadedSchema ? 'is_last_batch=1' : 'is_first_batch=1'); } if (newQueryParams.length > 0) { path += `?${newQueryParams.join('&')}`; } const ret = yield getData({ path, serverEndPoint }); if ('isAxiosError' in ret) { return false; } const dataRaw = 'results' in ret ? ret.results : ret; if ('results' in ret) { rowCount = ret.count; if (sumRows) { sumRowsTotals = ret.sum_rows; } } data = dataRaw.map((row) => { const newRow = {}; for (const [key, field] of Object.entries(schema)) { if (!(key in row)) { continue; } if (field.type === 'choice') { newRow[key] = row[key] ? { value: row[key], display_name: (0, utils_1.getChoiceByValue)(row[key], field.choices), } : (0, utils_1.emptyByType)(field); continue; } newRow[key] = row[key] ? row[key] : (0, utils_1.emptyByType)(field); } return newRow; }); } if (loadedSchema) { return { data, rowCount, sumRowsTotals }; } return { data, columns, schema, modelOptions, rowCount, sumRowsTotals }; }); exports.getGenericModelList = getGenericModelList; const getGenericModel = (_a) => __awaiter(void 0, [_a], void 0, function* ({ model, serverEndPoint, id = '', relatedModel = '', relatedModelId = '', }) { let path = `${model}/${id}`; let schemaPath = `${model}/`; if (!(0, utils_1.isTmpId)(id) && relatedModel) { path += `/${relatedModel}/`; if (relatedModelId) { path += `${relatedModelId}/`; } schemaPath += `${id}/${relatedModel}/`; } else if (id) { path += '/'; } const options = yield getSchema(schemaPath, serverEndPoint); if (!options) { return false; } const { schema, modelOptions } = options; const columns = getDataGridColumns(schema); if (!columns) { return false; } let data = {}; if (!(0, utils_1.isTmpId)(id) && (!relatedModel || (relatedModel && relatedModelId))) { const response = yield getData({ path, serverEndPoint }); if (!('isAxiosError' in response)) { data = response; } } return { schema, columns, modelOptions, data }; }); exports.getGenericModel = getGenericModel; const getAllModels = (serverEndPoint) => __awaiter(void 0, void 0, void 0, function* () { if (!serverEndPoint) { return { message: noServerEndPointMessage, isAxiosError: true, }; } const data = yield getData({ path: 'endpoints/', serverEndPoint }); return 'isAxiosError' in data ? [] : data; }); exports.getAllModels = getAllModels;