Loading capif_frontend/.env +1 −1 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ NEXT_PUBLIC_JWT_REFRESH_TOKEN_SECRET=7c4c1c50-3230-45bf-9eae-c9b2e401c767 # Peñuelas capif # NEXT_PUBLIC_API_BASE_URL="https://backoffice.mobilesandbox.cloud:38443/api" NEXT_PUBLIC_API_BASE_URL="http://localhost:8085/api" NEXT_PUBLIC_API_BASE_URL="https://backoffice.mobilesandbox.cloud:38443/api" NEXT_PUBLIC_GRAFANA_BASE_URL="https://grafana.5gnacar.int" NODE_ENV=development capif_frontend/src/configs/apiService.js +141 −46 Original line number Diff line number Diff line Loading @@ -10,22 +10,73 @@ export const getInvokers = async () => { } // GET invokers with pagination and date order export const getInvokersPagesOrder = async ({ page, pageSize, order }) => { export const getInvokersPagesOrder = async () => { try { const response = await axiosInstance.get(`/invokers/`, { const response = await axiosInstance.get(`/invokers/total`); return response; } catch (error) { throw new Error(`Error fetching invokers: ${error.message}`); } } export const getInvokerInformation = async invoker_id => { try { const response = await axiosInstance.get(`/invokers/alone`, { params: { page: page, pageSize: pageSize, order: order invoker_id: invoker_id, } }); return response.data; } catch (error) { throw new Error(`Error fetching invokers: ${error.message}`); throw new Error(`Error fetching invoker information: ${error.message}`); } } export const getProviderInformation = async api_prov_dom_id => { try { const response = await axiosInstance.get(`/providers/alone`, { params: { api_prov_dom_id: api_prov_dom_id, } }); return response.data; } catch (error) { throw new Error(`Error fetching provider information: ${error.message}`); } } export const getApisOfProvider = async apf_id => { try { const response = await axiosInstance.get(`/apis/provider`, { params: { apf_id: apf_id, } }); return response.data; } catch (error) { throw new Error(`Error fetching provider apis: ${error.message}`); } } export const getInvokerSecurityContext = async invoker_id => { try { const response = await axiosInstance.get(`/invokers/security`, { params: { invoker_id: invoker_id, } }); return response.data; } catch (error) { throw new Error(`Error fetching invoker's security context: ${error.message}`); } } // GET the num of invokers in the database export const getNumInvokers = async () => { return axiosInstance.get(`/invokers/numInvokers`) Loading Loading @@ -69,17 +120,12 @@ export const getProviders = async () => { return axiosInstance.get(`/providers/providersTotal`) } export const getProvidersPagesOrder = async ({ page, pageSize, order }) => { export const getProvidersPagesOrder = async () => { try { const response = await axiosInstance.get(`/providers/`, { params: { page: page, pageSize: pageSize, order: order } }); return response.data; return response; } catch (error) { throw new Error(`Error fetching providers: ${error.message}`); } Loading Loading @@ -128,49 +174,104 @@ export const getApis = async () => { return axiosInstance.get(`/apis/apisTotal`) } export const getApiInformation = async api_id => { try { const response = await axiosInstance.get(`/apis/alone`, { params: { api_id: api_id, } }); return response.data; } catch (error) { throw new Error(`Error fetching api information: ${error.message}`); } } // GET num of apis in the database export const getNumApis = async () => { return axiosInstance.get(`/apis/num`) } // GEt apis with pagination and date order export const getApisPagesOrder = async ({ page, pageSize, order }) => { export const getApisPagesOrder = async () => { try { const response = await axiosInstance.get(`/apis/`, { params: { page: page, pageSize: pageSize, order: order } }); const response = await axiosInstance.get(`/apis/`); return response.data; return response; } catch (error) { throw new Error(`Error fetching apis: ${error.message}`); } } // export const deleteProvidersServices = async (selectedApis) => { // try { // // Asegúrate de que el body envíe las APIs seleccionadas correctamente // const response = await axiosInstance.delete('/apis/', { // data: { // api: selectedApis, // }, // }); // return response.data; // } catch (error) { // console.error('Error deleting APIs:', error); // throw error; // } // }; // DELETE multiple apis export const deleteProvidersServices = async apis => { export const deleteProvidersServices = async (selectedApis) => { try { const data = { api: apis.map(({ apf_id, api_id }) => ({ apf_id, api_id })) }; const response = await axiosInstance.delete('/apis/', { headers: { 'Content-Type': 'application/json' }, data: JSON.stringify(data) data: { api: selectedApis } }); return response.data; // Retorna los datos de la respuesta si la solicitud fue exitosa return response.data; } catch (error) { throw error; // Lanza un error si la solicitud no fue exitosa console.error('Error deleting APIs:', error); throw error; } }; // DELETE multiple apis // export const deleteProvidersServices = async apis => { // try { // const data = { // api: apis.map(({ apf_id, api_id }) => ({ apf_id, api_id })) // }; // const response = await axiosInstance.delete('/apis/', { // headers: { // 'Content-Type': 'application/json' // }, // data: { // api: selectedApis, // }, // }); // return response.data; // Retorna los datos de la respuesta si la solicitud fue exitosa // } catch (error) { // throw error; // Lanza un error si la solicitud no fue exitosa // } // }; // DELETE multiple apis // const deleteApis = async (selectedApis) => { // try { // const response = await axiosInstance.delete('/apis/', { // data: { // api: selectedApis, // }, // }); // return response.data; // } catch (error) { // console.error('Error deleting APIs:', error.response ? error.response.data : error.message); // throw error; // } // }; Loading @@ -187,17 +288,11 @@ export const getUsers = async () => { return response.data; // Devolver solo los datos relevantes }; export const getUsersPagesOrder = async ({ page, pageSize, order }) => { export const getUsersPagesOrder = async () => { try { const response = await axiosInstance.get(`/users/`, { params: { page: page, pageSize: pageSize, order: order } }); const response = await axiosInstance.get(`/users/`); return response.data; return response; } catch (error) { throw new Error(`Error fetching users: ${error.message}`); } Loading capif_frontend/src/pages/apis/[id].js +89 −59 Original line number Diff line number Diff line Loading @@ -4,16 +4,12 @@ import Card from '@mui/material/Card' import Typography from '@mui/material/Typography' import CardHeader from '@mui/material/CardHeader' import PageHeader from 'src/@core/components/page-header' import Table from '@mui/material/Table' import TableContainer from '@mui/material/TableContainer' import CardContent from '@mui/material/CardContent' import { useRouter } from 'next/router' import { styled } from '@mui/material/styles' import { getApis } from 'src/configs/apiService' import { use, useEffect, useState } from 'react' import { useQuery, useQueryClient } from '@tanstack/react-query' import { set } from 'nprogress' import { getApiInformation } from 'src/configs/apiService' import { useEffect, useState } from 'react' import Box from '@mui/material/Box' const LinkStyled = styled(Link)(({ theme }) => ({ textDecoration: 'none', Loading @@ -22,74 +18,108 @@ const LinkStyled = styled(Link)(({ theme }) => ({ const Apis = () => { const router = useRouter() const apiName = router.query.id const api_id = router.query.id const [selectedApi, setSelectedApi] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Define la clave única para esta consulta const queryKey = ['apis']; // Usa useQuery para obtener los datos const { data: apisData, isLoading, isError, refetch } = useQuery({ queryKey, queryFn: getApis, }); const selectApi = () => { // Fetch API information useEffect(() => { if (api_id) { const fetchApiInformation = async () => { setLoading(true); setError(null); try { if (Array.isArray(apisData.data.object)) { // Filtra el array para encontrar el elemento deseado const selectedApi = apisData.data.object.find(api => api.api_name === apiName); setSelectedApi(selectedApi); if (selectedApi) { console.log('Selected API:', selectedApi); // Realiza acciones con el elemento seleccionado } else { console.warn('API not found:', apiName); } } else { console.error('Error: apisData is not an array'); console.error('apisData:', apisData); } } catch (error) { console.error('Error fetching data:', error); const apiData = await getApiInformation(api_id); // Llamada a la API setSelectedApi(apiData[0]); // Almacenar el primer elemento (asumiendo que devuelve un array) } catch (err) { setError("Error fetching API information"); console.error(err); } finally { setLoading(false); } }; useEffect(() => { selectApi(); }, [apiName, apisData]); fetchApiInformation(); } }, [api_id]); if (loading) return <Typography>Loading...</Typography>; if (error) return <Typography color="error">{error}</Typography>; return ( <Grid container spacing={6}> <PageHeader title={<Typography variant='h5'>API: {apiName} </Typography>} /> <PageHeader title={<Typography variant='h5'>API: {api_id} </Typography>} /> <Grid item xs={12}> <Card> <CardHeader title='Information' /> <CardContent> <p> Nombre: {apiName} </p> <p> API: {selectedApi?.api_id} </p> <p> APF: {selectedApi?.apf_id} </p> <p> AEF: {selectedApi?.aef_profiles[0].aef_id} </p> {selectedApi ? ( <Grid container spacing={2}> {/* Display API General Information */} <Grid item xs={12} mt={4}> <Typography variant="h6" gutterBottom><strong>General Information</strong></Typography> <Box mt={2}> <Typography><strong>API Name:</strong> {selectedApi.api_name}</Typography> <Typography><strong>API ID:</strong> {selectedApi.api_id}</Typography> <Typography><strong>Description:</strong> {selectedApi.description}</Typography> <Typography><strong>Onboarding Date:</strong> {selectedApi.onboarding_date}</Typography> <Typography><strong>Service API Category:</strong> {selectedApi.service_api_category || 'N/A'}</Typography> <Typography><strong>Supported Features:</strong> {selectedApi.supported_features}</Typography> </Box> </Grid> {/* Display AEF Profiles */} <Grid item xs={12} mt={6}> <Typography variant="h6" gutterBottom><strong>AEF Profiles</strong></Typography> {selectedApi.aef_profiles.map((profile, index) => ( <Box key={profile.aef_id} mb={4} mt={4}> <Typography><strong>AEF ID:</strong> {profile.aef_id}</Typography> <Typography><strong>Data Format:</strong> {profile.data_format}</Typography> <Typography><strong>Protocol:</strong> {profile.protocol}</Typography> {/* Interface Descriptions */} <Typography variant="h6" mt={4} gutterBottom><strong>Interface Descriptions</strong></Typography> {profile.interface_descriptions.map((desc, idx) => ( <Box key={idx} mb={2} mt={2}> <Typography><strong>IPv4 Address:</strong> {desc.ipv4_addr}</Typography> <Typography><strong>Port:</strong> {desc.port}</Typography> <Typography><strong>Security Methods:</strong> {desc.security_methods.join(", ")}</Typography> </Box> ))} {/* API Versions */} <Typography variant="h6" mt={4} gutterBottom><strong>Versions</strong></Typography> {profile.versions.map((version, idx) => ( <Box key={idx} mb={4} mt={2}> <Typography><strong>API Version:</strong> {version.api_version}</Typography> <Typography><strong>Expiry:</strong> {version.expiry}</Typography> {/* Resources */} <Typography variant="h6" mt={4} gutterBottom><strong>Resources</strong></Typography> {version.resources.map((resource, idx) => ( <Box key={idx} mb={2} mt={2}> <Typography><strong>Resource Name:</strong> {resource.resource_name}</Typography> <Typography><strong>URI:</strong> {resource.uri}</Typography> <Typography><strong>Operations:</strong> {resource.operations.join(", ")}</Typography> <Typography><strong>Description:</strong> {resource.description}</Typography> </Box> ))} </Box> ))} </Box> ))} </Grid> </Grid> ) : ( <Typography>No API data available.</Typography> )} </CardContent> </Card> </Grid> </Grid> ) } export default Apis export default Apis; capif_frontend/src/pages/apis/index.js +82 −97 Original line number Diff line number Diff line import React, { useState, useEffect } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { getApisPagesOrder, deleteProvidersServices, searchApis } from 'src/configs/apiService'; import Link from 'next/link'; import { styled } from '@mui/material/styles'; import { Loading @@ -27,6 +26,7 @@ import { DialogContent, DialogActions, } from '@mui/material'; import { getApisPagesOrder, deleteProvidersServices } from 'src/configs/apiService'; import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; Loading @@ -37,8 +37,6 @@ const LinkStyled = styled(Link)(({ theme }) => ({ const Apis = () => { const queryClient = useQueryClient(); const [selectedApis, setSelectedApis] = useState([]); const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(10); const [totalPages, setTotalPages] = useState(0); Loading @@ -46,106 +44,101 @@ const Apis = () => { const [apis, setApis] = useState([]); const [paginatedApis, setPaginatedApis] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [selectedApis, setSelectedApis] = useState([]); const [sortOrder, setSortOrder] = useState('asc'); const [isAscending, setIsAscending] = useState(true); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [showDeleteMessage, setShowDeleteMessage] = useState(false); const [errorDeleting, setErrorDeleting] = useState(false); const [deleteMessage, setDeleteMessage] = useState(''); // Obtener todos los datos de APIs const { data: apisData, isLoading, isError } = useQuery({ const { data: apisData, isLoading, isError, refetch } = useQuery({ queryKey: ['apis'], queryFn: () => getApisPagesOrder({ page: 1, pageSize: 1000 }), // Obtener todos los datos queryFn: getApisPagesOrder, // Obtener todos los datos }); useEffect(() => { if (apisData) { setApis(apisData.data); setTotalApis(apisData.total); setTotalPages(Math.ceil(apisData.data.length / pageSize)); // Paginar los datos if (apisData && Array.isArray(apisData.data)) { let filteredApis = apisData.data; // Aplicar filtro de búsqueda por api_id o api_name if (searchTerm) { filteredApis = filteredApis.filter((api) => api.api_id.toLowerCase().includes(searchTerm.toLowerCase()) || api.api_name?.toLowerCase().includes(searchTerm.toLowerCase()) ); } // Aplicar orden por fecha filteredApis.sort((a, b) => { const dateA = new Date(a.onboarding_date); const dateB = new Date(b.onboarding_date); return isAscending ? dateA - dateB : dateB - dateA; }); setTotalApis(filteredApis.length || 0); setTotalPages(Math.ceil(filteredApis.length / pageSize)); const start = (currentPage - 1) * pageSize; const end = start + pageSize; setPaginatedApis(apisData.data.slice(start, end)); setPaginatedApis(filteredApis.slice(start, end)); } }, [apisData, currentPage, pageSize]); }, [apisData, currentPage, pageSize, searchTerm, isAscending]); const handleToggleApi = (apf_id, api_id) => { const selectedIndex = selectedApis.findIndex(api => api.api_id === api_id); const handleToggleApi = (apiId, apfId) => { const selectedIndex = selectedApis.findIndex((api) => api.api_id === apiId); // Buscar por api_id let newSelected = []; if (selectedIndex === -1) { newSelected = [...selectedApis, { apf_id, api_id }]; newSelected = newSelected.concat(selectedApis, { api_id: apiId, apf_id: apfId }); } else if (selectedIndex === 0) { newSelected = selectedApis.slice(1); newSelected = newSelected.concat(selectedApis.slice(1)); } else if (selectedIndex === selectedApis.length - 1) { newSelected = selectedApis.slice(0, -1); newSelected = newSelected.concat(selectedApis.slice(0, -1)); } else if (selectedIndex > 0) { newSelected = [ ...selectedApis.slice(0, selectedIndex), ...selectedApis.slice(selectedIndex + 1), ]; newSelected = newSelected.concat( selectedApis.slice(0, selectedIndex), selectedApis.slice(selectedIndex + 1) ); } setSelectedApis(newSelected); }; const handleDeleteClick = () => { setDeleteDialogOpen(true); const handlePageChange = (event, newPage) => { setCurrentPage(newPage); }; const handleDeleteConfirm = async () => { try { setIsDeleting(true); setErrorDeleting(false); const handlePageSizeChange = (event) => { const newSize = event.target.value; setPageSize(newSize); setCurrentPage(1); // Reiniciar a la primera página cuando se cambie el tamaño de las filas }; await deleteProvidersServices(selectedApis); const handleClickDate = () => { setIsAscending((prevState) => !prevState); // Alternar el estado de orden }; setDeleteMessage('APIs removed'); // Refrescar la lista de APIs const response = await getApisPagesOrder({ page: currentPage, pageSize, order: sortOrder }); setApis(response.data); setTotalApis(response.total); setTotalPages(Math.ceil(response.total / pageSize)); const handleDeleteClick = () => { setIsDeleteDialogOpen(true); }; setDeleteDialogOpen(false); setSelectedApis([]); setShowDeleteMessage(true); const handleDeleteConfirm = async () => { setIsDeleting(true); try { await deleteProvidersServices(selectedApis); // Llamada para eliminar APIs console.log('APIs deleted:', selectedApis); setIsDeleteDialogOpen(false); // Cerrar el diálogo setSelectedApis([]); // Vaciar la selección refetch(); // Refrescar la lista de APIs } catch (error) { setErrorDeleting(true); console.error('Error deleting APIs:', error); } finally { setIsDeleting(false); } }; const handleDeleteCancel = () => { setDeleteDialogOpen(false); }; const handleDeleteMessageAccept = () => { setShowDeleteMessage(false); }; const handlePageChange = (event, newPage) => { setCurrentPage(newPage); const start = (newPage - 1) * pageSize; const end = start + pageSize; setPaginatedApis(apis.slice(start, end)); }; const handlePageSizeChange = (event) => { const newSize = event.target.value; setPageSize(newSize); setCurrentPage(1); const start = 0; const end = newSize; setPaginatedApis(apis.slice(start, end)); setTotalPages(Math.ceil(apis.length / newSize)); }; const handleClickDate = () => { setIsAscending((prevState) => !prevState); setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); setIsDeleteDialogOpen(false); // Cerrar el cuadro de diálogo sin hacer nada }; if (isLoading) return <CircularProgress />; Loading @@ -164,17 +157,13 @@ const Apis = () => { <Grid container spacing={2} alignItems="center" justifyContent="flex-end"> <Grid item> <TextField label="Search by ..." label="Search by ID or Name" variant="outlined" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} sx={{ width: '300px' }} /> </Grid> <Grid item> <Button variant="contained" color="primary"> Search </Button> </Grid> </Grid> </Grid> </Grid> Loading Loading @@ -204,17 +193,15 @@ const Apis = () => { <TableRow key={api.api_id}> <TableCell> <Checkbox onChange={() => handleToggleApi(api.apf_id, api.api_id)} checked={selectedApis.some( (selected) => selected.api_id === api.api_id && selected.apf_id === api.apf_id )} onChange={() => handleToggleApi(api.api_id, api.apf_id)} // Pasar api_id y apf_id checked={selectedApis.some((selected) => selected.api_id === api.api_id)} // Comprobar si está seleccionado /> </TableCell> <TableCell> <LinkStyled href={`/apis/${api.api_name}`}>{api.api_name}</LinkStyled> <LinkStyled href={`/apis/${api.api_id}`}>{api.api_name}</LinkStyled> </TableCell> <TableCell>{api.description}</TableCell> <TableCell>{api.aef_profiles[0].aef_id}</TableCell> <TableCell>{api.aef_profiles[0]?.aef_id}</TableCell> <TableCell>{api.onboarding_date}</TableCell> </TableRow> ))} Loading @@ -230,9 +217,14 @@ const Apis = () => { <Pagination count={totalPages} page={currentPage} onChange={handlePageChange} /> </Grid> <Grid> <Button variant="contained" color="primary" onClick={handleDeleteClick}> Delete {/* Botón para eliminar */} <Grid item> <Button variant="contained" onClick={handleDeleteClick} disabled={selectedApis.length === 0} // Deshabilitar si no hay selección > Delete Selected </Button> </Grid> Loading @@ -246,10 +238,11 @@ const Apis = () => { </Grid> </Grid> {/* Cuadro de diálogo de confirmación de eliminación */} <Dialog open={isDeleteDialogOpen} onClose={handleDeleteCancel}> <DialogTitle>Confirm Deletion</DialogTitle> <DialogContent> Are you sure you want to delete the following APIs? <Typography>Are you sure you want to delete the following APIs?</Typography> <ul> {selectedApis.map((api) => ( <li key={api.api_id}>{api.api_id}</li> Loading @@ -258,23 +251,15 @@ const Apis = () => { </DialogContent> <DialogActions> <Button onClick={handleDeleteCancel}>Cancel</Button> <Button onClick={handleDeleteConfirm} autoFocus disabled={isDeleting}> <Button onClick={handleDeleteConfirm} color="error" disabled={isDeleting} // Deshabilitar mientras se realiza la eliminación > {isDeleting ? <CircularProgress size={24} /> : 'Confirm'} </Button> </DialogActions> </Dialog> <Dialog open={showDeleteMessage} onClose={handleDeleteMessageAccept}> <DialogTitle>{errorDeleting ? 'Error Deleting API' : 'API Deleted!'}</DialogTitle> <DialogContent> {errorDeleting ? 'An error occurred while deleting the API.' : 'The APIs have been successfully deleted.'} </DialogContent> <DialogActions> <Button onClick={handleDeleteMessageAccept} autoFocus> Accept </Button> </DialogActions> </Dialog> </Grid> ); }; Loading capif_frontend/src/pages/invokers/[id].js +216 −145 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
capif_frontend/.env +1 −1 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ NEXT_PUBLIC_JWT_REFRESH_TOKEN_SECRET=7c4c1c50-3230-45bf-9eae-c9b2e401c767 # Peñuelas capif # NEXT_PUBLIC_API_BASE_URL="https://backoffice.mobilesandbox.cloud:38443/api" NEXT_PUBLIC_API_BASE_URL="http://localhost:8085/api" NEXT_PUBLIC_API_BASE_URL="https://backoffice.mobilesandbox.cloud:38443/api" NEXT_PUBLIC_GRAFANA_BASE_URL="https://grafana.5gnacar.int" NODE_ENV=development
capif_frontend/src/configs/apiService.js +141 −46 Original line number Diff line number Diff line Loading @@ -10,22 +10,73 @@ export const getInvokers = async () => { } // GET invokers with pagination and date order export const getInvokersPagesOrder = async ({ page, pageSize, order }) => { export const getInvokersPagesOrder = async () => { try { const response = await axiosInstance.get(`/invokers/`, { const response = await axiosInstance.get(`/invokers/total`); return response; } catch (error) { throw new Error(`Error fetching invokers: ${error.message}`); } } export const getInvokerInformation = async invoker_id => { try { const response = await axiosInstance.get(`/invokers/alone`, { params: { page: page, pageSize: pageSize, order: order invoker_id: invoker_id, } }); return response.data; } catch (error) { throw new Error(`Error fetching invokers: ${error.message}`); throw new Error(`Error fetching invoker information: ${error.message}`); } } export const getProviderInformation = async api_prov_dom_id => { try { const response = await axiosInstance.get(`/providers/alone`, { params: { api_prov_dom_id: api_prov_dom_id, } }); return response.data; } catch (error) { throw new Error(`Error fetching provider information: ${error.message}`); } } export const getApisOfProvider = async apf_id => { try { const response = await axiosInstance.get(`/apis/provider`, { params: { apf_id: apf_id, } }); return response.data; } catch (error) { throw new Error(`Error fetching provider apis: ${error.message}`); } } export const getInvokerSecurityContext = async invoker_id => { try { const response = await axiosInstance.get(`/invokers/security`, { params: { invoker_id: invoker_id, } }); return response.data; } catch (error) { throw new Error(`Error fetching invoker's security context: ${error.message}`); } } // GET the num of invokers in the database export const getNumInvokers = async () => { return axiosInstance.get(`/invokers/numInvokers`) Loading Loading @@ -69,17 +120,12 @@ export const getProviders = async () => { return axiosInstance.get(`/providers/providersTotal`) } export const getProvidersPagesOrder = async ({ page, pageSize, order }) => { export const getProvidersPagesOrder = async () => { try { const response = await axiosInstance.get(`/providers/`, { params: { page: page, pageSize: pageSize, order: order } }); return response.data; return response; } catch (error) { throw new Error(`Error fetching providers: ${error.message}`); } Loading Loading @@ -128,49 +174,104 @@ export const getApis = async () => { return axiosInstance.get(`/apis/apisTotal`) } export const getApiInformation = async api_id => { try { const response = await axiosInstance.get(`/apis/alone`, { params: { api_id: api_id, } }); return response.data; } catch (error) { throw new Error(`Error fetching api information: ${error.message}`); } } // GET num of apis in the database export const getNumApis = async () => { return axiosInstance.get(`/apis/num`) } // GEt apis with pagination and date order export const getApisPagesOrder = async ({ page, pageSize, order }) => { export const getApisPagesOrder = async () => { try { const response = await axiosInstance.get(`/apis/`, { params: { page: page, pageSize: pageSize, order: order } }); const response = await axiosInstance.get(`/apis/`); return response.data; return response; } catch (error) { throw new Error(`Error fetching apis: ${error.message}`); } } // export const deleteProvidersServices = async (selectedApis) => { // try { // // Asegúrate de que el body envíe las APIs seleccionadas correctamente // const response = await axiosInstance.delete('/apis/', { // data: { // api: selectedApis, // }, // }); // return response.data; // } catch (error) { // console.error('Error deleting APIs:', error); // throw error; // } // }; // DELETE multiple apis export const deleteProvidersServices = async apis => { export const deleteProvidersServices = async (selectedApis) => { try { const data = { api: apis.map(({ apf_id, api_id }) => ({ apf_id, api_id })) }; const response = await axiosInstance.delete('/apis/', { headers: { 'Content-Type': 'application/json' }, data: JSON.stringify(data) data: { api: selectedApis } }); return response.data; // Retorna los datos de la respuesta si la solicitud fue exitosa return response.data; } catch (error) { throw error; // Lanza un error si la solicitud no fue exitosa console.error('Error deleting APIs:', error); throw error; } }; // DELETE multiple apis // export const deleteProvidersServices = async apis => { // try { // const data = { // api: apis.map(({ apf_id, api_id }) => ({ apf_id, api_id })) // }; // const response = await axiosInstance.delete('/apis/', { // headers: { // 'Content-Type': 'application/json' // }, // data: { // api: selectedApis, // }, // }); // return response.data; // Retorna los datos de la respuesta si la solicitud fue exitosa // } catch (error) { // throw error; // Lanza un error si la solicitud no fue exitosa // } // }; // DELETE multiple apis // const deleteApis = async (selectedApis) => { // try { // const response = await axiosInstance.delete('/apis/', { // data: { // api: selectedApis, // }, // }); // return response.data; // } catch (error) { // console.error('Error deleting APIs:', error.response ? error.response.data : error.message); // throw error; // } // }; Loading @@ -187,17 +288,11 @@ export const getUsers = async () => { return response.data; // Devolver solo los datos relevantes }; export const getUsersPagesOrder = async ({ page, pageSize, order }) => { export const getUsersPagesOrder = async () => { try { const response = await axiosInstance.get(`/users/`, { params: { page: page, pageSize: pageSize, order: order } }); const response = await axiosInstance.get(`/users/`); return response.data; return response; } catch (error) { throw new Error(`Error fetching users: ${error.message}`); } Loading
capif_frontend/src/pages/apis/[id].js +89 −59 Original line number Diff line number Diff line Loading @@ -4,16 +4,12 @@ import Card from '@mui/material/Card' import Typography from '@mui/material/Typography' import CardHeader from '@mui/material/CardHeader' import PageHeader from 'src/@core/components/page-header' import Table from '@mui/material/Table' import TableContainer from '@mui/material/TableContainer' import CardContent from '@mui/material/CardContent' import { useRouter } from 'next/router' import { styled } from '@mui/material/styles' import { getApis } from 'src/configs/apiService' import { use, useEffect, useState } from 'react' import { useQuery, useQueryClient } from '@tanstack/react-query' import { set } from 'nprogress' import { getApiInformation } from 'src/configs/apiService' import { useEffect, useState } from 'react' import Box from '@mui/material/Box' const LinkStyled = styled(Link)(({ theme }) => ({ textDecoration: 'none', Loading @@ -22,74 +18,108 @@ const LinkStyled = styled(Link)(({ theme }) => ({ const Apis = () => { const router = useRouter() const apiName = router.query.id const api_id = router.query.id const [selectedApi, setSelectedApi] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Define la clave única para esta consulta const queryKey = ['apis']; // Usa useQuery para obtener los datos const { data: apisData, isLoading, isError, refetch } = useQuery({ queryKey, queryFn: getApis, }); const selectApi = () => { // Fetch API information useEffect(() => { if (api_id) { const fetchApiInformation = async () => { setLoading(true); setError(null); try { if (Array.isArray(apisData.data.object)) { // Filtra el array para encontrar el elemento deseado const selectedApi = apisData.data.object.find(api => api.api_name === apiName); setSelectedApi(selectedApi); if (selectedApi) { console.log('Selected API:', selectedApi); // Realiza acciones con el elemento seleccionado } else { console.warn('API not found:', apiName); } } else { console.error('Error: apisData is not an array'); console.error('apisData:', apisData); } } catch (error) { console.error('Error fetching data:', error); const apiData = await getApiInformation(api_id); // Llamada a la API setSelectedApi(apiData[0]); // Almacenar el primer elemento (asumiendo que devuelve un array) } catch (err) { setError("Error fetching API information"); console.error(err); } finally { setLoading(false); } }; useEffect(() => { selectApi(); }, [apiName, apisData]); fetchApiInformation(); } }, [api_id]); if (loading) return <Typography>Loading...</Typography>; if (error) return <Typography color="error">{error}</Typography>; return ( <Grid container spacing={6}> <PageHeader title={<Typography variant='h5'>API: {apiName} </Typography>} /> <PageHeader title={<Typography variant='h5'>API: {api_id} </Typography>} /> <Grid item xs={12}> <Card> <CardHeader title='Information' /> <CardContent> <p> Nombre: {apiName} </p> <p> API: {selectedApi?.api_id} </p> <p> APF: {selectedApi?.apf_id} </p> <p> AEF: {selectedApi?.aef_profiles[0].aef_id} </p> {selectedApi ? ( <Grid container spacing={2}> {/* Display API General Information */} <Grid item xs={12} mt={4}> <Typography variant="h6" gutterBottom><strong>General Information</strong></Typography> <Box mt={2}> <Typography><strong>API Name:</strong> {selectedApi.api_name}</Typography> <Typography><strong>API ID:</strong> {selectedApi.api_id}</Typography> <Typography><strong>Description:</strong> {selectedApi.description}</Typography> <Typography><strong>Onboarding Date:</strong> {selectedApi.onboarding_date}</Typography> <Typography><strong>Service API Category:</strong> {selectedApi.service_api_category || 'N/A'}</Typography> <Typography><strong>Supported Features:</strong> {selectedApi.supported_features}</Typography> </Box> </Grid> {/* Display AEF Profiles */} <Grid item xs={12} mt={6}> <Typography variant="h6" gutterBottom><strong>AEF Profiles</strong></Typography> {selectedApi.aef_profiles.map((profile, index) => ( <Box key={profile.aef_id} mb={4} mt={4}> <Typography><strong>AEF ID:</strong> {profile.aef_id}</Typography> <Typography><strong>Data Format:</strong> {profile.data_format}</Typography> <Typography><strong>Protocol:</strong> {profile.protocol}</Typography> {/* Interface Descriptions */} <Typography variant="h6" mt={4} gutterBottom><strong>Interface Descriptions</strong></Typography> {profile.interface_descriptions.map((desc, idx) => ( <Box key={idx} mb={2} mt={2}> <Typography><strong>IPv4 Address:</strong> {desc.ipv4_addr}</Typography> <Typography><strong>Port:</strong> {desc.port}</Typography> <Typography><strong>Security Methods:</strong> {desc.security_methods.join(", ")}</Typography> </Box> ))} {/* API Versions */} <Typography variant="h6" mt={4} gutterBottom><strong>Versions</strong></Typography> {profile.versions.map((version, idx) => ( <Box key={idx} mb={4} mt={2}> <Typography><strong>API Version:</strong> {version.api_version}</Typography> <Typography><strong>Expiry:</strong> {version.expiry}</Typography> {/* Resources */} <Typography variant="h6" mt={4} gutterBottom><strong>Resources</strong></Typography> {version.resources.map((resource, idx) => ( <Box key={idx} mb={2} mt={2}> <Typography><strong>Resource Name:</strong> {resource.resource_name}</Typography> <Typography><strong>URI:</strong> {resource.uri}</Typography> <Typography><strong>Operations:</strong> {resource.operations.join(", ")}</Typography> <Typography><strong>Description:</strong> {resource.description}</Typography> </Box> ))} </Box> ))} </Box> ))} </Grid> </Grid> ) : ( <Typography>No API data available.</Typography> )} </CardContent> </Card> </Grid> </Grid> ) } export default Apis export default Apis;
capif_frontend/src/pages/apis/index.js +82 −97 Original line number Diff line number Diff line import React, { useState, useEffect } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { getApisPagesOrder, deleteProvidersServices, searchApis } from 'src/configs/apiService'; import Link from 'next/link'; import { styled } from '@mui/material/styles'; import { Loading @@ -27,6 +26,7 @@ import { DialogContent, DialogActions, } from '@mui/material'; import { getApisPagesOrder, deleteProvidersServices } from 'src/configs/apiService'; import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; Loading @@ -37,8 +37,6 @@ const LinkStyled = styled(Link)(({ theme }) => ({ const Apis = () => { const queryClient = useQueryClient(); const [selectedApis, setSelectedApis] = useState([]); const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(10); const [totalPages, setTotalPages] = useState(0); Loading @@ -46,106 +44,101 @@ const Apis = () => { const [apis, setApis] = useState([]); const [paginatedApis, setPaginatedApis] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [selectedApis, setSelectedApis] = useState([]); const [sortOrder, setSortOrder] = useState('asc'); const [isAscending, setIsAscending] = useState(true); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [showDeleteMessage, setShowDeleteMessage] = useState(false); const [errorDeleting, setErrorDeleting] = useState(false); const [deleteMessage, setDeleteMessage] = useState(''); // Obtener todos los datos de APIs const { data: apisData, isLoading, isError } = useQuery({ const { data: apisData, isLoading, isError, refetch } = useQuery({ queryKey: ['apis'], queryFn: () => getApisPagesOrder({ page: 1, pageSize: 1000 }), // Obtener todos los datos queryFn: getApisPagesOrder, // Obtener todos los datos }); useEffect(() => { if (apisData) { setApis(apisData.data); setTotalApis(apisData.total); setTotalPages(Math.ceil(apisData.data.length / pageSize)); // Paginar los datos if (apisData && Array.isArray(apisData.data)) { let filteredApis = apisData.data; // Aplicar filtro de búsqueda por api_id o api_name if (searchTerm) { filteredApis = filteredApis.filter((api) => api.api_id.toLowerCase().includes(searchTerm.toLowerCase()) || api.api_name?.toLowerCase().includes(searchTerm.toLowerCase()) ); } // Aplicar orden por fecha filteredApis.sort((a, b) => { const dateA = new Date(a.onboarding_date); const dateB = new Date(b.onboarding_date); return isAscending ? dateA - dateB : dateB - dateA; }); setTotalApis(filteredApis.length || 0); setTotalPages(Math.ceil(filteredApis.length / pageSize)); const start = (currentPage - 1) * pageSize; const end = start + pageSize; setPaginatedApis(apisData.data.slice(start, end)); setPaginatedApis(filteredApis.slice(start, end)); } }, [apisData, currentPage, pageSize]); }, [apisData, currentPage, pageSize, searchTerm, isAscending]); const handleToggleApi = (apf_id, api_id) => { const selectedIndex = selectedApis.findIndex(api => api.api_id === api_id); const handleToggleApi = (apiId, apfId) => { const selectedIndex = selectedApis.findIndex((api) => api.api_id === apiId); // Buscar por api_id let newSelected = []; if (selectedIndex === -1) { newSelected = [...selectedApis, { apf_id, api_id }]; newSelected = newSelected.concat(selectedApis, { api_id: apiId, apf_id: apfId }); } else if (selectedIndex === 0) { newSelected = selectedApis.slice(1); newSelected = newSelected.concat(selectedApis.slice(1)); } else if (selectedIndex === selectedApis.length - 1) { newSelected = selectedApis.slice(0, -1); newSelected = newSelected.concat(selectedApis.slice(0, -1)); } else if (selectedIndex > 0) { newSelected = [ ...selectedApis.slice(0, selectedIndex), ...selectedApis.slice(selectedIndex + 1), ]; newSelected = newSelected.concat( selectedApis.slice(0, selectedIndex), selectedApis.slice(selectedIndex + 1) ); } setSelectedApis(newSelected); }; const handleDeleteClick = () => { setDeleteDialogOpen(true); const handlePageChange = (event, newPage) => { setCurrentPage(newPage); }; const handleDeleteConfirm = async () => { try { setIsDeleting(true); setErrorDeleting(false); const handlePageSizeChange = (event) => { const newSize = event.target.value; setPageSize(newSize); setCurrentPage(1); // Reiniciar a la primera página cuando se cambie el tamaño de las filas }; await deleteProvidersServices(selectedApis); const handleClickDate = () => { setIsAscending((prevState) => !prevState); // Alternar el estado de orden }; setDeleteMessage('APIs removed'); // Refrescar la lista de APIs const response = await getApisPagesOrder({ page: currentPage, pageSize, order: sortOrder }); setApis(response.data); setTotalApis(response.total); setTotalPages(Math.ceil(response.total / pageSize)); const handleDeleteClick = () => { setIsDeleteDialogOpen(true); }; setDeleteDialogOpen(false); setSelectedApis([]); setShowDeleteMessage(true); const handleDeleteConfirm = async () => { setIsDeleting(true); try { await deleteProvidersServices(selectedApis); // Llamada para eliminar APIs console.log('APIs deleted:', selectedApis); setIsDeleteDialogOpen(false); // Cerrar el diálogo setSelectedApis([]); // Vaciar la selección refetch(); // Refrescar la lista de APIs } catch (error) { setErrorDeleting(true); console.error('Error deleting APIs:', error); } finally { setIsDeleting(false); } }; const handleDeleteCancel = () => { setDeleteDialogOpen(false); }; const handleDeleteMessageAccept = () => { setShowDeleteMessage(false); }; const handlePageChange = (event, newPage) => { setCurrentPage(newPage); const start = (newPage - 1) * pageSize; const end = start + pageSize; setPaginatedApis(apis.slice(start, end)); }; const handlePageSizeChange = (event) => { const newSize = event.target.value; setPageSize(newSize); setCurrentPage(1); const start = 0; const end = newSize; setPaginatedApis(apis.slice(start, end)); setTotalPages(Math.ceil(apis.length / newSize)); }; const handleClickDate = () => { setIsAscending((prevState) => !prevState); setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); setIsDeleteDialogOpen(false); // Cerrar el cuadro de diálogo sin hacer nada }; if (isLoading) return <CircularProgress />; Loading @@ -164,17 +157,13 @@ const Apis = () => { <Grid container spacing={2} alignItems="center" justifyContent="flex-end"> <Grid item> <TextField label="Search by ..." label="Search by ID or Name" variant="outlined" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} sx={{ width: '300px' }} /> </Grid> <Grid item> <Button variant="contained" color="primary"> Search </Button> </Grid> </Grid> </Grid> </Grid> Loading Loading @@ -204,17 +193,15 @@ const Apis = () => { <TableRow key={api.api_id}> <TableCell> <Checkbox onChange={() => handleToggleApi(api.apf_id, api.api_id)} checked={selectedApis.some( (selected) => selected.api_id === api.api_id && selected.apf_id === api.apf_id )} onChange={() => handleToggleApi(api.api_id, api.apf_id)} // Pasar api_id y apf_id checked={selectedApis.some((selected) => selected.api_id === api.api_id)} // Comprobar si está seleccionado /> </TableCell> <TableCell> <LinkStyled href={`/apis/${api.api_name}`}>{api.api_name}</LinkStyled> <LinkStyled href={`/apis/${api.api_id}`}>{api.api_name}</LinkStyled> </TableCell> <TableCell>{api.description}</TableCell> <TableCell>{api.aef_profiles[0].aef_id}</TableCell> <TableCell>{api.aef_profiles[0]?.aef_id}</TableCell> <TableCell>{api.onboarding_date}</TableCell> </TableRow> ))} Loading @@ -230,9 +217,14 @@ const Apis = () => { <Pagination count={totalPages} page={currentPage} onChange={handlePageChange} /> </Grid> <Grid> <Button variant="contained" color="primary" onClick={handleDeleteClick}> Delete {/* Botón para eliminar */} <Grid item> <Button variant="contained" onClick={handleDeleteClick} disabled={selectedApis.length === 0} // Deshabilitar si no hay selección > Delete Selected </Button> </Grid> Loading @@ -246,10 +238,11 @@ const Apis = () => { </Grid> </Grid> {/* Cuadro de diálogo de confirmación de eliminación */} <Dialog open={isDeleteDialogOpen} onClose={handleDeleteCancel}> <DialogTitle>Confirm Deletion</DialogTitle> <DialogContent> Are you sure you want to delete the following APIs? <Typography>Are you sure you want to delete the following APIs?</Typography> <ul> {selectedApis.map((api) => ( <li key={api.api_id}>{api.api_id}</li> Loading @@ -258,23 +251,15 @@ const Apis = () => { </DialogContent> <DialogActions> <Button onClick={handleDeleteCancel}>Cancel</Button> <Button onClick={handleDeleteConfirm} autoFocus disabled={isDeleting}> <Button onClick={handleDeleteConfirm} color="error" disabled={isDeleting} // Deshabilitar mientras se realiza la eliminación > {isDeleting ? <CircularProgress size={24} /> : 'Confirm'} </Button> </DialogActions> </Dialog> <Dialog open={showDeleteMessage} onClose={handleDeleteMessageAccept}> <DialogTitle>{errorDeleting ? 'Error Deleting API' : 'API Deleted!'}</DialogTitle> <DialogContent> {errorDeleting ? 'An error occurred while deleting the API.' : 'The APIs have been successfully deleted.'} </DialogContent> <DialogActions> <Button onClick={handleDeleteMessageAccept} autoFocus> Accept </Button> </DialogActions> </Dialog> </Grid> ); }; Loading
capif_frontend/src/pages/invokers/[id].js +216 −145 File changed.Preview size limit exceeded, changes collapsed. Show changes