1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/web/ synced 2025-03-09 00:00:01 +01:00

updated redux storage and added RTK Query

Signed-off-by: Andrii Podriez <andrey5577990@gmail.com>
This commit is contained in:
Andrii Podriez 2024-07-25 13:54:16 +02:00 committed by al3xa23
parent 3ca56016a4
commit 8faab6613e
4 changed files with 474 additions and 34 deletions

306
src/store/apiSlice.js Normal file
View file

@ -0,0 +1,306 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { sessionToken } from '../localStorage';
export const apiSlice = createApi({
reducerPath: 'scenarios',
baseQuery: fetchBaseQuery({
baseUrl: '/api/v2',
prepareHeaders: (headers) => {
const token = sessionToken;
if (token) {
headers.set('Authorization', `Bearer ${token}`);
}
return headers;
},
}),
endpoints: (builder) => ({
getScenarios: builder.query({
query: () => 'scenarios',
}),
getScenarioById: builder.query({
query: (id) => `scenarios/${id}`,
}),
addScenario: builder.mutation({
query: (scenario) => ({
url: 'scenarios',
method: 'POST',
body: scenario,
}),
}),
deleteScenario: builder.mutation({
query: (id) => ({
url: `scenarios/${id}`,
method: 'DELETE',
}),
}),
updateScenario: builder.mutation({
query: ({ id, ...updatedScenario }) => ({
url: `scenarios/${id}`,
method: 'PUT',
body: updatedScenario,
}),
}),
getConfigs: builder.query({
query: (scenarioID) => `configs?scenarioID=${scenarioID}`,
}),
getUsersOfScenario: builder.query({
query: (scenarioID) => `scenarios/${scenarioID}/users/`,
}),
getDashboards: builder.query({
query: (scenarioID) => `dashboards?scenarioID=${scenarioID}`,
}),
getICS: builder.query({
query: () => 'ic',
}),
addUserToScenario: builder.mutation({
query: ({ scenarioID, username }) => {
return ({
url: `scenarios/${scenarioID}/user?username=${username}`,
method: 'PUT',
})},
}),
removeUserFromScenario: builder.mutation({
query: ({ scenarioID, username }) => ({
url: `scenarios/${scenarioID}/user/?username=${username}`,
method: 'DELETE',
}),
}),
addComponentConfig: builder.mutation({
query: (config) => ({
url: 'configs',
method: 'POST',
body: config,
}),
}),
deleteComponentConfig: builder.mutation({
query: (configID) => ({
url: `configs/${configID}`,
method: 'DELETE',
}),
}),
addDashboard: builder.mutation({
query: (dashboard) => ({
url: 'dashboards',
method: 'POST',
body: dashboard,
}),
}),
deleteDashboard: builder.mutation({
query: (dashboardID) => ({
url: `dashboards/${dashboardID}`,
method: 'DELETE',
}),
}),
updateDashboard: builder.mutation({
query: ({ dashboardID, dashboard }) => ({
url: `dashboards/${dashboardID}`,
method: 'PUT',
body: {dashboard},
}),
}),
getSignals: builder.query({
query: ({ direction, configID }) => ({
url: 'signals',
params: { direction, configID },
}),
}),
addSignal: builder.mutation({
query: (signal) => ({
url: 'signals',
method: 'POST',
body: { signal },
}),
}),
deleteSignal: builder.mutation({
query: (signalID) => ({
url: `signals/${signalID}`,
method: 'DELETE',
}),
}),
//users
getUsers: builder.query({
query: () => 'users',
}),
getUserById: builder.query({
query: (id) => `users/${id}`,
}),
addUser: builder.mutation({
query: (user) => ({
url: 'users',
method: 'POST',
body: user,
}),
}),
updateUser: builder.mutation({
query: (user) => {
return {
url: `users/${user.id}`,
method: 'PUT',
body: {user: user},
}},
}),
deleteUser: builder.mutation({
query: (id) => ({
url: `users/${id}`,
method: 'DELETE',
}),
}),
//results
getResults: builder.query({
query: (scenarioID) => ({
url: 'results',
params: { scenarioID },
}),
}),
addResult: builder.mutation({
query: (result) => ({
url: 'results',
method: 'POST',
body: result,
}),
}),
deleteResult: builder.mutation({
query: (resultID) => ({
url: `results/${resultID}`,
method: 'DELETE',
}),
}),
//files
getFiles: builder.query({
query: (scenarioID) => ({
url: 'files',
params: { scenarioID },
}),
}),
addFile: builder.mutation({
query: ({ scenarioID, file }) => {
const formData = new FormData();
formData.append('inputFile', file);
return {
url: `files?scenarioID=${scenarioID}`,
method: 'POST',
body: formData,
};
},
}),
downloadFile: builder.query({
query: (fileID) => ({
url: `files/${fileID}`,
responseHandler: 'blob',
responseType: 'blob',
}),
}),
updateFile: builder.mutation({
query: ({ fileID, file }) => {
const formData = new FormData();
formData.append('inputFile', file);
return {
url: `files/${fileID}`,
method: 'PUT',
body: formData,
};
},
}),
deleteFile: builder.mutation({
query: (fileID) => ({
url: `files/${fileID}`,
method: 'DELETE',
}),
}),
sendAction: builder.mutation({
query: (params) => ({
url: `/ic/${params.icid}/action`,
method: 'POST',
body: [params],
}),
}),
getDashboard: builder.query({
query: (dashboardID) => `/dashboards/${dashboardID}`,
}),
getWidgets: builder.query({
query: (dashboardID) => ({
url: 'widgets',
params: { dashboardID },
}),
}),
addWidget: builder.mutation({
query: (widget) => ({
url: 'widgets',
method: 'POST',
body: { widget },
}),
}),
getWidget: builder.query({
query: (widgetID) => `/widgets/${widgetID}`,
}),
updateWidget: builder.mutation({
query: ({ widgetID, updatedWidget }) => ({
url: `/widgets/${widgetID}`,
method: 'PUT',
body: updatedWidget,
}),
}),
deleteWidget: builder.mutation({
query: (widgetID) => ({
url: `/widgets/${widgetID}`,
method: 'DELETE',
}),
}),
getConfig: builder.query({
query: () => '/config',
})
}),
});
export const {
useGetScenariosQuery,
useGetScenarioByIdQuery,
useGetConfigsQuery,
useLazyGetConfigsQuery,
useGetDashboardsQuery,
useGetICSQuery,
useAddScenarioMutation,
useDeleteScenarioMutation,
useUpdateScenarioMutation,
useGetUsersOfScenarioQuery,
useAddUserToScenarioMutation,
useRemoveUserFromScenarioMutation,
useAddComponentConfigMutation,
useDeleteComponentConfigMutation,
useAddDashboardMutation,
useDeleteDashboardMutation,
useLazyGetSignalsQuery,
useGetSignalsQuery,
useAddSignalMutation,
useDeleteSignalMutation,
useGetResultsQuery,
useAddResultMutation,
useDeleteResultMutation,
useGetUsersQuery,
useGetUserByIdQuery,
useAddUserMutation,
useUpdateUserMutation,
useDeleteUserMutation,
useGetFilesQuery,
useAddFileMutation,
useLazyDownloadFileQuery,
useUpdateFileMutation,
useDeleteFileMutation,
useGetDashboardQuery,
useUpdateDashboardMutation,
useSendActionMutation,
useAddWidgetMutation,
useLazyGetWidgetsQuery,
useUpdateWidgetMutation,
useDeleteWidgetMutation,
useGetConfigQuery,
} = apiSlice;

View file

@ -17,25 +17,67 @@
import {createSlice, createAsyncThunk} from '@reduxjs/toolkit'
import RestAPI from '../common/api/rest-api';
import { sessionToken } from '../localStorage';
import NotificationsDataManager from '../common/data-managers/notifications-data-manager';
import NotificationsFactory from '../common/data-managers/notifications-factory';
const icSlice = createSlice({
name: 'infrastructure',
initialState: {
ICsArray: [],
checkedICsArray: [],
checkedICsIds: [],
isLoading: false,
currentIC: {},
isCurrentICLoading: false
isCurrentICLoading: false,
//IC used for Edit and Delete Modals
editModalIC: null,
deleteModalIC: null,
isDeleteModalOpened: false,
isEditModalOpened: false
},
reducers: {
checkICsByCategory: (state, args) => {
const category = args.payload;
updateCheckedICs: (state, args) => {
// each table has an object that maps IDs of all its ICs to boolean values
// which indicates wether or note user picked it in checbox column
const checkboxValues = args.payload;
let checkedICsIds = [...state.checkedICsIds];
for(const ic in state.ICsArray){
if (ic.category == category) state.checkedICsArray.push(ic)
for(const id in checkboxValues){
if(checkedICsIds.includes(id)){
if(!checkboxValues[id]){
checkedICsIds = checkedICsIds.filter((checkedId) => checkedId != id);
}
} else {
if(checkboxValues[id]){
checkedICsIds.push(id);
}
}
}
state.checkedICsIds = checkedICsIds;
},
clearCheckedICs: (state, args) => {
state.checkedICsIds = [];
},
openEditModal: (state, args) => {
state.isEditModalOpened = true;
state.editModalIC = args.payload;
console.log(state.editModalIC)
},
closeEditModal: (state, args) => {
state.isEditModalOpened = false;
state.editModalIC = null;
},
openDeleteModal: (state, args) => {
state.deleteModalIC = args.payload;
state.isDeleteModalOpened = true;
},
closeDeleteModal: (state, args) => {
state.deleteModalIC = null;
state.isDeleteModalOpened = false;
}
},
extraReducers: builder => {
@ -50,20 +92,33 @@ const icSlice = createSlice({
.addCase(loadICbyId.pending, (state, action) => {
state.isCurrentICLoading = true
})
.addCase(loadICbyId.fulfilled, (state, action) => {
state.isCurrentICLoading = false
state.currentIC = action.payload;
console.log("fetched IC", state.currentIC.name)
})
//TODO
// .addCase(restartIC.fullfilled, (state, action) => {
// console.log("restart fullfilled")
// //loadAllICs({token: sessionToken})
// })
// .addCase(shutdownIC.fullfilled, (state, action) => {
// console.log("shutdown fullfilled")
// //loadAllICs({token: sessionToken})
// })
.addCase(loadICbyId.fulfilled, (state, action) => {
state.isCurrentICLoading = false
state.currentIC = action.payload;
console.log("fetched IC", state.currentIC.name)
})
.addCase(addIC.rejected, (state, action) => {
NotificationsDataManager.addNotification(NotificationsFactory.ADD_ERROR("Error while adding infrastructural component: " + action.error.message));
})
.addCase(sendActionToIC.rejected, (state, action) => {
NotificationsDataManager.addNotification(NotificationsFactory.ADD_ERROR("Error while sending action to infrastructural component: " + action.error.message));
})
.addCase(editIC.rejected, (state, action) => {
NotificationsDataManager.addNotification(NotificationsFactory.ADD_ERROR("Error while trying to update an infrastructural component: " + action.error.message));
})
.addCase(deleteIC.rejected, (state, action) => {
NotificationsDataManager.addNotification(NotificationsFactory.ADD_ERROR("Error while trying to delete an infrastructural component: " + action.error.message));
})
//TODO
// .addCase(restartIC.fullfilled, (state, action) => {
// console.log("restart fullfilled")
// //loadAllICs({token: sessionToken})
// })
// .addCase(shutdownIC.fullfilled, (state, action) => {
// console.log("shutdown fullfilled")
// //loadAllICs({token: sessionToken})
// })
}
});
@ -93,6 +148,85 @@ export const loadICbyId = createAsyncThunk(
}
)
//adds a new Infrastructural component. Data object must contain token and ic fields
export const addIC = createAsyncThunk(
'infrastructure/addIC',
async (data, {rejectWithValue}) => {
try {
//post request body: ic object that is to be added
const ic = {ic: data.ic};
const res = await RestAPI.post('/api/v2/ic/', ic, data.token);
return res;
} catch (error) {
console.log("Error adding IC: ", error);
return rejectWithValue(error.response.data);
}
}
)
//sends an action to IC. Data object must contain a token, IC's id and actions string
export const sendActionToIC = createAsyncThunk(
'infrastructure/sendActionToIC',
async (data, {rejectWithValue}) => {
try {
const token = data.token;
const id = data.id;
let actions = data.actions;
console.log("actions: ", actions)
if (!Array.isArray(actions))
actions = [ actions ]
for (let action of actions) {
if (action.when) {
// Send timestamp as Unix Timestamp
action.when = Math.round(new Date(action.when).getTime() / 1000);
}
}
const res = await RestAPI.post('/api/v2/ic/'+id+'/action', actions, token);
NotificationsDataManager.addNotification(NotificationsFactory.ACTION_INFO());
return res;
} catch (error) {
console.log("Error sending an action to IC: ", error);
return rejectWithValue(error.response.data);
}
}
)
//send a request to update IC's data. Data object must contain token, and updated ic object
export const editIC = createAsyncThunk(
'infrastructure/editIC',
async (data, {rejectWithValue}) => {
try {
//post request body: ic object that is to be added
const {token, ic} = data;
const res = await RestAPI.put('/api/v2/ic/'+ic.id, {ic: ic}, token);
return res;
} catch (error) {
return rejectWithValue(error.response.data);
}
}
)
//send a request to delete IC. Data object must contain token, and id of the IC that is to be deleted
export const deleteIC = createAsyncThunk(
'infrastructure/deleteIC',
async (data, {rejectWithValue}) => {
try {
//post request body: ic object that is to be added
const {token, id} = data;
const res = await RestAPI.delete('/api/v2/ic/'+id, token);
return res;
} catch (error) {
console.log("Error updating IC: ", error);
return rejectWithValue(error.response.data);
}
}
)
//TODO
//restarts ICs
@ -110,7 +244,8 @@ export const restartIC = createAsyncThunk(
}
)
//restarts ICs
//shut ICs down
export const shutdownIC = createAsyncThunk(
'infrastructure/shutdownIC',
async (data) => {
@ -125,6 +260,8 @@ export const shutdownIC = createAsyncThunk(
}
)
export const {checkICsByCategory} = icSlice.actions;
export default icSlice.reducer;
export const {updateCheckedICs, clearCheckedICs, openEditModal, openDeleteModal, closeDeleteModal, closeEditModal} = icSlice.actions;
export default icSlice.reducer;

View file

@ -16,16 +16,19 @@
******************************************************************************/
import { configureStore } from "@reduxjs/toolkit";
import userReducer from './userSlice';
import icReducer from './icSlice';
import configReducer from './configSlice'
import { apiSlice } from "./apiSlice";
export const store = configureStore({
reducer: {
user: userReducer,
infrastructure: icReducer,
config: configReducer
config: configReducer,
[apiSlice.reducerPath]: apiSlice.reducer,
},
devTools: true
})
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(apiSlice.middleware),
devTools: true,
})

View file

@ -16,9 +16,7 @@
******************************************************************************/
import {createSlice, createAsyncThunk} from '@reduxjs/toolkit'
import RestAPI from '../common/api/rest-api';
import ICDataDataManager from '../ic/ic-data-data-manager';
const userSlice = createSlice({
name: 'user',
@ -91,12 +89,8 @@ export const loginExternal = createAsyncThunk(
export const logout = createAsyncThunk(
'user/logout',
async () => {
// disconnect from all infrastructure components
ICDataDataManager.closeAll();
//remove token and current user from local storage
localStorage.clear();
console.log("logged out")
}
)