Skip to content
Snippets Groups Projects
Commit 60e7f1d4 authored by Leonardo Christino's avatar Leonardo Christino
Browse files

Merge branch 'alert' into 'main'

feat(alert): add server error alerts

See merge request !50
parents 646fb112 44d32a8d
No related branches found
No related tags found
1 merge request!50feat(alert): add server error alerts
Pipeline #127080 passed
Showing
with 65 additions and 99 deletions
......@@ -12,7 +12,7 @@ import {
} from '@graphpolaris/shared/lib/data-access';
import { WebSocketHandler } from '@graphpolaris/shared/lib/data-access/socket';
import Broker from '@graphpolaris/shared/lib/data-access/socket/broker';
import { assignNewGraphQueryResult, useAppDispatch, useML, useMLEnabledHash } from '@graphpolaris/shared/lib/data-access/store';
import { assignNewGraphQueryResult, useAppDispatch, useConfig, useML, useMLEnabledHash } from '@graphpolaris/shared/lib/data-access/store';
import {
GraphQueryResultFromBackend,
GraphQueryResultFromBackendPayload,
......@@ -28,7 +28,8 @@ import { logout } from '@graphpolaris/shared/lib/data-access/store/authSlice';
import { SchemaFromBackend } from '@graphpolaris/shared/lib/schema';
import { LinkPredictionInstance, setMLResult, allMLTypes } from '@graphpolaris/shared/lib/data-access/store/mlSlice';
import { Resizable } from '@graphpolaris/shared/lib/components/Resizable';
import { BrokerAlerts } from '@graphpolaris/shared/lib/data-access/socket/broker/brokerAlerts';
import { DashboardAlerts } from '@graphpolaris/shared/lib/data-access/authorization/dashboardAlerts';
import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice';
export interface App {}
......@@ -46,6 +47,7 @@ export function App(props: App) {
const [authCheck, setAuthCheck] = useState(false);
const ml = useML();
const mlHash = useMLEnabledHash();
const config = useConfig();
// for testing purposes
// useEffect(() => {
......@@ -83,7 +85,9 @@ export function App(props: App) {
// console.log('Auth changed', auth.authorized, isLogin);
if (auth.authorized) {
console.debug('App is authorized; Getting Databases', isLogin);
api.GetAllDatabases({ updateSessionCache: true });
api.GetAllDatabases({ updateSessionCache: true }).catch((e) => {
dispatch(addError(e));
});
setAuthCheck(true);
} else {
// dispatch(logout());
......@@ -106,7 +110,7 @@ export function App(props: App) {
return (
<div className="h-screen w-screen">
<BrokerAlerts />
<DashboardAlerts />
<div className={'h-screen w-screen ' + (!auth.authorized ? 'blur-sm pointer-events-none ' : '')}>
<div className="flex flex-col h-screen max-h-screen relative">
<aside className="h-[4rem]">
......
......@@ -3,13 +3,16 @@ import {
DatabaseType,
databaseNameMapping,
databaseProtocolMapping,
useAppDispatch,
useDatabaseAPI,
useSchemaAPI,
} from '@graphpolaris/shared/lib/data-access';
import React, { useEffect, useRef, useState } from 'react';
import { RequiredInput } from './requiredinput';
import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice';
export const NewDatabaseForm = (props: { onClose(): void; open: boolean }) => {
const dispatch = useAppDispatch();
const ref = useRef<HTMLDialogElement>(null);
const [state, setState] = useState<AddDatabaseRequest>({
// username: 'root',
......@@ -67,9 +70,14 @@ export const NewDatabaseForm = (props: { onClose(): void; open: boolean }) => {
function handleSubmitClicked(): void {
ref.current?.close();
if (!Object.values(hasError).some((e) => e === true)) {
api.AddDatabase(state, { updateDatabaseCache: true, setAsCurrent: true }).then(() => {
schemaApi.RequestSchema(state.name);
});
api
.AddDatabase(state, { updateDatabaseCache: true, setAsCurrent: true })
.then(() => {
schemaApi.RequestSchema(state.name);
})
.catch((e) => {
dispatch(addError(e));
});
}
// props.onSubmit(state);
}
......@@ -77,7 +85,7 @@ export const NewDatabaseForm = (props: { onClose(): void; open: boolean }) => {
return (
<dialog ref={ref}>
<form
className="card flex gap-4 p-5 rounded-sm shadow-lg"
className="card flex gap-4 p-4 rounded-sm"
onSubmit={(event: React.FormEvent) => {
event.preventDefault();
handleSubmitClicked();
......
......@@ -24,6 +24,7 @@ import {
} from '@graphpolaris/shared/lib/data-access';
import { DatabaseMenu } from './databasemenu';
import { NewDatabaseForm } from './AddDatabaseForm/newdatabaseform';
import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice';
/** NavbarComponentProps is an interface containing the NavbarViewModel. */
export interface NavbarComponentProps {
......@@ -62,9 +63,14 @@ export const Navbar = (props: NavbarComponentProps) => {
* Called when the user clicks on the 'submit' button of the add database form.
*/
function onAddDatabaseFormSubmit(request: AddDatabaseRequest): Promise<void | Response> {
return api.AddDatabase(request, { updateDatabaseCache: true, setAsCurrent: true }).then(() => {
schemaApi.RequestSchema(request.name);
});
return api
.AddDatabase(request, { updateDatabaseCache: true, setAsCurrent: true })
.then(() => {
schemaApi.RequestSchema(request.name);
})
.catch((e) => {
dispatch(addError(e));
});
}
const currentLogo = !'dark' ? logo_white : logo; // TODO: support dark mode
......@@ -156,7 +162,9 @@ export const Navbar = (props: NavbarComponentProps) => {
if (session.currentDatabase === db) {
dispatch(updateCurrentDatabase(''));
}
api.DeleteDatabase(db);
api.DeleteDatabase(db).catch((e) => {
dispatch(addError(e));
});
setSubMenuOpen(undefined);
setMenuOpen(false);
}}
......
......@@ -59,7 +59,7 @@ export const useDatabaseAPI = () => {
reject(response.statusText);
}
if (setAsCurrent) dispatch(updateCurrentDatabase(request.name));
if (updateDatabaseCache) GetAllDatabases({ updateSessionCache: true });
if (updateDatabaseCache) GetAllDatabases({ updateSessionCache: true }).catch(reject);
resolve();
});
......@@ -81,8 +81,7 @@ export const useDatabaseAPI = () => {
if (!response.ok) {
const text = await response.text();
console.error(text);
return [];
// throw Error(response.statusText)
throw Error(response.statusText);
}
const json = await response.json();
if (updateDatabaseCache) dispatch(updateDatabaseList(json.databases));
......@@ -103,7 +102,7 @@ export const useDatabaseAPI = () => {
reject(response.statusText);
}
if (updateDatabaseCache) GetAllDatabases({ updateSessionCache: true });
if (updateDatabaseCache) GetAllDatabases({ updateSessionCache: true }).catch(reject);
resolve();
});
......
export * from './database';
export * from './user';
export * from './schema';
export * from './query';
......@@ -22,7 +22,7 @@ export const useQueryAPI = () => {
if (!response?.ok) {
const ret = await response.text();
console.error(response, ret);
return;
throw Error(response.statusText);
}
const ret = await response.json();
console.debug('Sent Query EXECUTION', ret);
......
// All user related API calls
import { useAuthorizationCache } from '../store';
export type User = {
Name: string;
Email: string;
SignInProvider: number;
};
export const useUserAPI = () => {
const { accessToken } = useAuthorizationCache();
const domain = import.meta.env.BACKEND_URL;
function GetUserInfo(): Promise<User> {
return new Promise<User>((resolve, reject) => {
fetch(`${domain}/user/`, {
method: 'GET',
credentials: 'same-origin',
headers: new Headers({
Authorization: 'Bearer ' + accessToken,
}),
})
.then((response: Response) => {
if (!response.ok) {
reject(response.statusText);
}
return response.json();
})
.then((json: any) => {
resolve({
Name: json.name,
Email: json.email,
SignInProvider: json.sign_in_provider,
});
});
});
}
return { GetUserInfo };
};
import React, { ReactNode, useEffect, useState } from 'react';
import Broker from '@graphpolaris/shared/lib/data-access/socket/broker';
import { useImmer } from 'use-immer';
import styles from './brokerAlerts.module.scss';
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
type Message = {
......@@ -9,7 +8,7 @@ type Message = {
className: string;
};
export const BrokerAlerts = (props: { timer?: number }) => {
export const DashboardAlerts = (props: { timer?: number }) => {
const [messages, setMessages] = useImmer<{ data: any; routingKey: string; message: Message; showing: boolean }[]>([]);
const timer = props.timer || 1200;
......
......@@ -18,7 +18,7 @@ export default class BackendMessenger implements BackendMessengerRepository {
/**
* Sends a fetch request to the Datastrophe domain.
* @param {string} body The request body you want to send. Should be stringified JSON.
* @param {string} requestURL The URL you want to perform this request to, appended to 'datastrophe.science.uu.nl'.
* @param {string} requestURL The URL you want to perform this request'.
* @param {string} requestMethod The method of your request. Most used are: POST, GET.
* @returns {Promise<void>} A promise which is resolved once a response with status 200 has been received.
*/
......@@ -41,7 +41,7 @@ export default class BackendMessenger implements BackendMessengerRepository {
/**
* Sendrequest sends a GET request to the backend.
* @param requestURL The URL you want to perform this request to, appended to 'datastrophe.science.uu.nl'.
* @param requestURL The URL you want to perform this request'.
* @returns {Promise<void>} A promise which is resolved once a response with status 200 has been received.
*/
public SendRequest(requestURL: string): Promise<Response> {
......
......@@ -2,23 +2,43 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from './store';
// Define the initial state using that type
export const initialState = {
export const initialState: {
queryListOpen: boolean;
queryStatusList: {
queries: Record<string, any>;
queryIDsOrder: string[];
};
functionsMenuOpen: boolean;
currentDatabaseKey: string;
elementsperDatabaseObject: Record<string, number>;
autoSendQueries: boolean;
errors: string[];
} = {
queryListOpen: false,
queryStatusList: { queries: {}, queryIDsOrder: [] },
functionsMenuOpen: false,
currentDatabaseKey: '',
elementsperDatabaseObject: {},
autoSendQueries: true,
errors: [],
};
export const configSlice = createSlice({
name: 'config',
// `createSlice` will infer the state type from the `initialState` argument
initialState,
reducers: {},
reducers: {
addError: (state, action: PayloadAction<string>) => {
console.error('Error Received!', action.payload);
state.errors.push(action.payload);
},
removeLastError: (state) => {
state.errors.shift();
},
},
});
export const {} = configSlice.actions;
export const { addError, removeLastError } = configSlice.actions;
// Other code such as selectors can use the imported `RootState` type
export const configState = (state: RootState) => state.config;
......
......@@ -289,7 +289,7 @@ export function Query2BackendQuery(
return ret;
});
console.info('New query', graph, query);
console.debug('New processed query', graph, query);
return query;
}
......
......@@ -52,6 +52,8 @@ export const NodeLinkVis = React.memo((props: Props) => {
useEffect(() => {
if (graphQueryResult) {
console.log('graphQueryResult', graphQueryResult);
setGraph(
parseQueryResult(graphQueryResult, ml, {
defaultX: (ref.current?.clientWidth || 1000) / 2,
......@@ -64,17 +66,6 @@ export const NodeLinkVis = React.memo((props: Props) => {
const onClickedNode = (node: NodeType, ml: ML) => {
console.log('shortestPath', graph, ml.shortestPath.enabled);
if (graph) {
// //If node is already clicked we should remove it from the list.
// if (highlightNodes.includes(node)) {
// const index = highlightNodes.indexOf(node, 0);
// setHighlightNodes(highlightNodes.splice(index, 1));
// } else {
// //Else add it to the list.
// setHighlightNodes(highlightNodes.concat(node));
// }
// //Update the list of edges that need to be highlighted
// setHighlightedLinks(getRelatedLinks(graph, highlightNodes, ml.communityDetection.jaccard_threshold || -1));
if (ml.shortestPath.enabled) {
console.log('shortestPath');
......@@ -107,28 +98,8 @@ export const NodeLinkVis = React.memo((props: Props) => {
}
};
// useEffect(() => {
// if (ml && graph) {
// // const g = processML(ml, graph as GraphType);
// // console.log(g);
// setGraph((draft) => {
// const g = processML(ml, draft as GraphType);
// return g;
// });
// }
// }, [ml]);
return (
<>
{/* <input
onChange={(v) =>
dispatch(changePrimary({ main: v.currentTarget.value }))
}
type="color"
name="head"
value={theme.palette.primary.main}
/> */}
<div className="h-full w-full overflow-hidden" ref={ref}>
<NLPixi
graph={graph}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment