Skip to content

Commit

Permalink
refat(api): decouple api logic from pages
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolearagao committed Jan 22, 2024
1 parent 007f61f commit 862f1b5
Show file tree
Hide file tree
Showing 18 changed files with 420 additions and 243 deletions.
4 changes: 2 additions & 2 deletions src/app/appLayout/AppToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
ToolbarItem
} from '@patternfly/react-core';
import { EllipsisVIcon, MoonIcon, QuestionCircleIcon, SunIcon } from '@patternfly/react-icons';
import axios from 'axios';
import apiService from 'src/services/apiService';
import { useUsername } from '../../components/sessionContext/SessionProvider';
import '@patternfly/react-styles/css/components/Avatar/avatar.css';
import './AppToolbar.css';
Expand Down Expand Up @@ -44,7 +44,7 @@ const AppToolbar: React.FunctionComponent = () => {
const onAbout = () => {};

const onLogout = () => {
axios
apiService
.put('https://0.0.0.0:9443/api/v1/users/logout/')
.catch(err => {
console.error('Failed to logout', err);
Expand Down
4 changes: 2 additions & 2 deletions src/app/routes.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import { Route, Routes } from 'react-router';
import { Navigate, useLocation, useNavigate } from 'react-router-dom';
import axios from 'axios';
import apiService from 'src/services/apiService';
import { Login } from '../pages/login/Login';
import NotFound from '../pages/notFound/NotFound';

Expand Down Expand Up @@ -59,7 +59,7 @@ const AppRoutes = (): React.ReactElement => {
const nav = useNavigate();
const location = useLocation();

axios.get('https://0.0.0.0:9443/api/v1/users/current/').catch(() => {
apiService.get('https://0.0.0.0:9443/api/v1/users/current/').catch(() => {
if (location.pathname !== '/login') {
nav('/login');
}
Expand Down
22 changes: 22 additions & 0 deletions src/common/alertsHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { AlertProps } from '@patternfly/react-core';

const useAlerts = () => {
const [alert, setAlerts] = React.useState<Partial<AlertProps>[]>([]);

const addAlert = (title: string, variant: AlertProps['variant'], key: React.Key) => {
setAlerts(prevAlerts => [...prevAlerts, { title, variant, key }]);
};

const removeAlert = (key: React.Key) => {
setAlerts(prevAlerts => [...prevAlerts.filter(alert => alert.key !== key)]);
};

return {
removeAlert,
addAlert,
alerts: alert
};
};

export default useAlerts;
4 changes: 2 additions & 2 deletions src/components/sessionContext/SessionProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
import apiService from 'src/services/apiService';
import { helpers } from '../../common';

const WHO_AM_I_QUERY = 'whoami';
Expand Down Expand Up @@ -49,7 +49,7 @@ const SessionProvider: React.FC<{ children: React.ReactNode }> = ({ children })
queryKey: [WHO_AM_I_QUERY],
refetchOnWindowFocus: !helpers.DEV_MODE,
queryFn: () => {
return axios.get(process.env.REACT_APP_USER_SERVICE_CURRENT || '').then(res => {
return apiService.get(process.env.REACT_APP_USER_SERVICE_CURRENT || '').then(res => {
if (res.data.username) {
setSessionData(prev => ({
...prev,
Expand Down
13 changes: 11 additions & 2 deletions src/constants/apiConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ const API_SUBMIT_SOURCE_OPTIONS_DISABLE_SSL = 'disable_ssl';
const API_SUBMIT_SOURCE_OPTIONS_PARAMIKO = 'use_paramiko';
const API_SUBMIT_SOURCE_PORT = 'port';
const API_SUBMIT_SOURCE_SOURCE_TYPE = 'source_type';
const API_SOURCES_LIST_QUERY = 'sourcesList';
const API_CREDS_LIST_QUERY = 'credentialsList';
const API_SCANS_LIST_QUERY = 'scansList';

const apiTypes = {
API_QUERY_TYPES,
Expand Down Expand Up @@ -266,7 +269,10 @@ const apiTypes = {
API_SUBMIT_SOURCE_OPTIONS_DISABLE_SSL,
API_SUBMIT_SOURCE_OPTIONS_PARAMIKO,
API_SUBMIT_SOURCE_PORT,
API_SUBMIT_SOURCE_SOURCE_TYPE
API_SUBMIT_SOURCE_SOURCE_TYPE,
API_SOURCES_LIST_QUERY,
API_CREDS_LIST_QUERY,
API_SCANS_LIST_QUERY
};

export {
Expand Down Expand Up @@ -382,5 +388,8 @@ export {
API_SUBMIT_SOURCE_OPTIONS_DISABLE_SSL,
API_SUBMIT_SOURCE_OPTIONS_PARAMIKO,
API_SUBMIT_SOURCE_PORT,
API_SUBMIT_SOURCE_SOURCE_TYPE
API_SUBMIT_SOURCE_SOURCE_TYPE,
API_SOURCES_LIST_QUERY,
API_CREDS_LIST_QUERY,
API_SCANS_LIST_QUERY
};
40 changes: 40 additions & 0 deletions src/hooks/api/useCredentialApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { API_CREDS_LIST_QUERY } from 'src/constants/apiConstants';
import apiHandler from 'src/services/apiUtils';
import { CredentialType } from 'src/types';
import apiService from '../../services/apiService';
import useApiConfig from '../apiService/apiConfig';
import usePendingState from '../common/usePendingState';

const useCredentialApi = () => {
const { pendingItem: pendingDeleteCredential, setPendingItem: setPendingDeleteCredential } =
usePendingState<CredentialType>();
const selectedItems = [];
const { addAlert, queryClient } = useApiConfig();

const onDeleteCredential = (credential: CredentialType) => {
apiHandler(
apiService.delete,
`https://0.0.0.0:9443/api/v1/credentials/${credential.id}/`,
null,
`Credential "${credential.name}" deleted successfully`,
`Error removing credential ${credential.name}.`,
[API_CREDS_LIST_QUERY],
addAlert,
queryClient
).finally(() => setPendingDeleteCredential(undefined));
};

const onDeleteSelectedCredentials = () => {
const itemsToDelete = Object.values(selectedItems).filter(val => val !== null);
// add logic
console.log('Deleting selected credentials:', itemsToDelete);
};
return {
onDeleteCredential,
onDeleteSelectedCredentials,
pendingDeleteCredential,
setPendingDeleteCredential
};
};

export default useCredentialApi;
55 changes: 55 additions & 0 deletions src/hooks/api/useScanApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import { API_SCANS_LIST_QUERY } from 'src/constants/apiConstants';
import apiHandler from 'src/services/apiUtils';
import { ScanType } from 'src/types';
import apiService from '../../services/apiService';
import useApiConfig from '../apiService/apiConfig';
import usePendingState from '../common/usePendingState';

const useScanApi = () => {
const { addAlert, queryClient } = useApiConfig();
const { pendingItem: pendingDeleteScan, setPendingItem: setPendingDeleteScan } =
usePendingState<ScanType>();
const [scanSelected, setScanSelected] = React.useState<ScanType>();

const onDeleteScan = (scan: ScanType) => {
apiHandler(
apiService.delete,
`https://0.0.0.0:9443/api/v1/scans/${scan.id}/`,
null,
`Scan "${scan.id}" deleted successfully`,
`Error removing scan ${scan.id}`,
[API_SCANS_LIST_QUERY],
addAlert,
queryClient
).finally(() => setPendingDeleteScan(undefined));
};

const onRunScan = payload => {
apiHandler(
apiService.post,
process.env.REACT_APP_SCANS_SERVICE,
payload,
`${payload.name} started to scan`,
`Error starting scan.`,
[API_SCANS_LIST_QUERY],
addAlert,
queryClient
);
};

// const onDeleteSelectedItems = () => {
// // add logic
// const selectedItems = [];
// console.log('Deleting selected credentials:', selectedItems);
// };
return {
onDeleteScan,
onRunScan,
pendingDeleteScan,
setPendingDeleteScan,
scanSelected,
setScanSelected
};
};
export default useScanApi;
141 changes: 141 additions & 0 deletions src/hooks/api/useSourceApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React from 'react';
import { API_SOURCES_LIST_QUERY } from 'src/constants/apiConstants';
import apiHandler from 'src/services/apiUtils';
import { ConnectionType, SourceType } from 'src/types';
import apiService from '../../services/apiService';
import useApiConfig from '../apiService/apiConfig';
import usePendingState from '../common/usePendingState';

const useSourceApi = () => {
const [scanSelected, setScanSelected] = React.useState<SourceType[]>();
const [addSourceModal, setAddSourceModal] = React.useState<string>();
const [sourceBeingEdited, setSourceBeingEdited] = React.useState<SourceType>();
const { pendingItem: pendingDeleteSource, setPendingItem: setPendingDeleteSource } =
usePendingState<SourceType>();
const [connectionsData, setConnectionsData] = React.useState<{
successful: ConnectionType[];
failure: ConnectionType[];
unreachable: ConnectionType[];
}>({ successful: [], failure: [], unreachable: [] });
const [connectionsSelected, setConnectionsSelected] = React.useState<SourceType>();
const emptyConnectionData = { successful: [], failure: [], unreachable: [] };
const { addAlert, queryClient } = useApiConfig();

const onRunScan = payload => {
apiHandler(
apiService.post,
`https://0.0.0.0:9443/api/v1/scans/`,
payload,
`${payload.name} started to scan`,
`Error starting scan.`,
[API_SOURCES_LIST_QUERY],
addAlert,
queryClient
);
};

const onAddSource = (payload: SourceType) => {
apiHandler(
apiService.post,
`https://0.0.0.0:9443/api/v1/sources/?scan=true`,
payload,
`${payload.name} added successfully`,
null,
[API_SOURCES_LIST_QUERY],
addAlert,
queryClient
);
};

const onSubmitEditedSource = (payload: SourceType) => {
apiHandler(
apiService.put,
`https://0.0.0.0:9443/api/v1/sources/${payload.id}/`,
payload,
`${payload.name} updated successfully`,
null,
[API_SOURCES_LIST_QUERY],
addAlert,
queryClient
);
};

const onDeleteSource = (source: SourceType) => {
apiHandler(
apiService.delete,
`https://0.0.0.0:9443/api/v1/sources/${source.id}/`,
null,
`Source "${source.name}" deleted successfully`,
`Error removing source ${source.name}.`,
[API_SOURCES_LIST_QUERY],
addAlert,
queryClient
).finally(() => setPendingDeleteSource(undefined));
};

const showConnections = (source: SourceType) => {
apiService
.get(
`https://0.0.0.0:9443/api/v1/jobs/${source.connection.id}/connection/?page=1&page_size=1000&ordering=name&source_type=${source.id}`
)
.then(res => {
setConnectionsData({
successful: res.data.results.filter((c: { status: string }) => c.status === 'success'),
failure: res.data.results.filter((c: { status: string }) => c.status === 'failure'),
unreachable: res.data.results.filter(
(c: { status: string }) => !['success', 'failure'].includes(c.status)
)
});
})
.catch(err => console.error(err));
setConnectionsSelected(source);
};

const onEditSource = (source: SourceType) => {
setSourceBeingEdited(source);
};

const onDeleteSelectedSources = () => {
// add logic
const selectedItems = [];
console.log('Deleting selected credentials:', selectedItems);
};

const onCloseConnections = () => {
setConnectionsSelected(undefined);
setConnectionsData(emptyConnectionData);
};

const onScanSources = items => {
setScanSelected(items);
};

const onScanSource = (source: SourceType) => {
setScanSelected([source]);
};

return {
onRunScan,
onAddSource,
onSubmitEditedSource,
onDeleteSource,
showConnections,
onEditSource,
onScanSources,
scanSelected,
setScanSelected,
onDeleteSelectedSources,
onCloseConnections,
onScanSource,
addSourceModal,
setAddSourceModal,
sourceBeingEdited,
setSourceBeingEdited,
pendingDeleteSource,
setPendingDeleteSource,
connectionsData,
connectionsSelected
};
};

export default useSourceApi;
10 changes: 10 additions & 0 deletions src/hooks/apiService/apiConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import useAlerts from 'src/common/alertsHelper';
import useQueryClientConfig from 'src/services/queryClientConfig';

const useApiConfig = () => {
const { addAlert } = useAlerts();
const { queryClient } = useQueryClientConfig();

return { addAlert, queryClient };
};
export default useApiConfig;
8 changes: 8 additions & 0 deletions src/hooks/common/usePendingState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';

const usePendingState = <T>() => {
const [pendingItem, setPendingItem] = React.useState<T>();

return { pendingItem, setPendingItem };
};
export default usePendingState;
11 changes: 0 additions & 11 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,6 @@ if (process.env.NODE_ENV !== 'production') {
const root = ReactDOM.createRoot(document.getElementById('root') as Element);
const queryClient = new QueryClient();

//TODO: just to get token manually until we have login screen
// axios
// .post('https://0.0.0.0:9443/api/v1/token/', {
// username: 'admin',
// password: 'pleasechangethispassword'
// })
// .then(res => {
// localStorage.setItem('authToken', res.data.token);
// console.log('Token', res.data.token);
// });

root.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
Expand Down
Loading

0 comments on commit 862f1b5

Please sign in to comment.