Merge pull request 'Adding Consume Functionality' (#74) from consume-functionality into main
Some checks failed
checks-impure / test (push) Successful in 26s
checks / test (push) Successful in 1m10s
assets1 / test (push) Has been cancelled

Reviewed-on: #74
This commit was merged in pull request #74.
This commit is contained in:
Sara Pervana
2024-01-29 19:38:27 +01:00
8 changed files with 277 additions and 37 deletions

View File

@@ -106,11 +106,13 @@ async def consume_service_from_other_entity_c1() -> HTMLResponse:
@app_c1.get("/v1/print_daemon1/register", response_class=JSONResponse) @app_c1.get("/v1/print_daemon1/register", response_class=JSONResponse)
async def register_c1() -> JSONResponse: async def register_c1() -> JSONResponse:
time.sleep(2)
return JSONResponse(content={"status": "registered"}, status_code=200) return JSONResponse(content={"status": "registered"}, status_code=200)
@app_c1.get("/v1/print_daemon1/deregister", response_class=JSONResponse) @app_c1.get("/v1/print_daemon1/deregister", response_class=JSONResponse)
async def deregister_c1() -> JSONResponse: async def deregister_c1() -> JSONResponse:
time.sleep(2)
return JSONResponse(content={"status": "deregistered"}, status_code=200) return JSONResponse(content={"status": "deregistered"}, status_code=200)
@@ -130,11 +132,13 @@ async def consume_service_from_other_entity_c2() -> HTMLResponse:
@app_c2.get("/v1/print_daemon2/register", response_class=JSONResponse) @app_c2.get("/v1/print_daemon2/register", response_class=JSONResponse)
async def register_c2() -> JSONResponse: async def register_c2() -> JSONResponse:
time.sleep(2)
return JSONResponse(content={"status": "registered"}, status_code=200) return JSONResponse(content={"status": "registered"}, status_code=200)
@app_c2.get("/v1/print_daemon2/deregister", response_class=JSONResponse) @app_c2.get("/v1/print_daemon2/deregister", response_class=JSONResponse)
async def deregister_c2() -> JSONResponse: async def deregister_c2() -> JSONResponse:
time.sleep(2)
return JSONResponse(content={"status": "deregistered"}, status_code=200) return JSONResponse(content={"status": "deregistered"}, status_code=200)

View File

@@ -24,6 +24,7 @@ import CloseIcon from "@mui/icons-material/Close";
import { useSearchParams } from "next/navigation"; import { useSearchParams } from "next/navigation";
import SummaryDetails from "@/components/summary_card"; import SummaryDetails from "@/components/summary_card";
import { projectConfig } from "@/config/config"; import { projectConfig } from "@/config/config";
import ConsumeDisplayComponent from "@/components/consume_content";
interface SnackMessage { interface SnackMessage {
message: string; message: string;
@@ -105,8 +106,12 @@ const AttachButton = ({
export default function Client() { export default function Client() {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
console.log("params: ", searchParams);
const name = searchParams.get("name") ?? ""; const name = searchParams.get("name") ?? "";
const [consumeContent, setConsumeContent] = useState(null);
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState<
SnackMessage | undefined
>(undefined);
const { entity: entity } = useGetEntityByNameOrDid(name); const { entity: entity } = useGetEntityByNameOrDid(name);
const { const {
@@ -139,16 +144,16 @@ export default function Client() {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState<
SnackMessage | undefined
>(undefined);
const closeSnackBar = () => { const closeSnackBar = () => {
setSnackbarMessage(undefined); setSnackbarMessage(undefined);
setSnackbarOpen(false); setSnackbarOpen(false);
}; };
// Consume
const handleConsumeContent = (content: any) => {
setConsumeContent(content);
};
if (services_loading) return <Skeleton height={500} />; if (services_loading) return <Skeleton height={500} />;
if (!services) return <Alert severity="error">Client not found</Alert>; if (!services) return <Alert severity="error">Client not found</Alert>;
@@ -185,17 +190,33 @@ export default function Client() {
], ],
}} }}
/> />
<div> <div
<h4>Client View</h4> style={{
<CustomTable display: "flex",
loading={services_loading} justifyContent: "space-between",
data={clients} flexWrap: "nowrap",
configuration={ClientTableConfig} alignItems: "center",
tkey="client-table" }}
/> >
<div style={{ width: consumeContent ? "55%" : "100%" }}>
<h4>Service Consumer View</h4>
<CustomTable
loading={services_loading}
data={clients}
onConsumeAction={handleConsumeContent}
configuration={ClientTableConfig}
tkey="client-table"
/>
</div>
{consumeContent && (
<div style={{ width: "40%" }}>
<h4>Service Output</h4>
<ConsumeDisplayComponent htmlContent={consumeContent} />
</div>
)}
</div> </div>
<div> <div>
<h4>Service View</h4> <h4>Service Producer View</h4>
<CustomTable <CustomTable
loading={services_loading} loading={services_loading}
data={services?.data} data={services?.data}

View File

@@ -0,0 +1,73 @@
import { Button, CircularProgress, Snackbar } from "@mui/material";
import { useState } from "react";
import axios from "axios";
const ConsumeAction = ({
endpoint,
onConsume,
}: {
endpoint: string;
rowData?: any;
onConsume?: any;
}) => {
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
if (error) console.error("Error in state", error);
const handleConsume = () => {
if (loading) return;
setLoading(true);
const axiosConfig = {
url: endpoint,
method: "GET",
data: null,
withCredentials: true,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
};
axios(axiosConfig)
.then((response) => {
if (onConsume) {
onConsume(response.data);
console.log("I got the data from consume: ", response.data);
}
})
.catch((error) => {
if (onConsume) onConsume(null);
console.error("Error happened during consume: ", error);
setError(error);
})
.finally(() => {
setLoading(false);
});
};
const handleCloseSnackbar = () => {
setError(null);
};
return (
<>
<Button disabled={loading} onClick={handleConsume} variant="outlined">
{loading ? <CircularProgress size={24} /> : `Consume`}
</Button>
{error && (
<Snackbar
anchorOrigin={{ vertical: "top", horizontal: "center" }}
open={error}
autoHideDuration={2000}
message={`Something happened during consume: ${error}`}
onClose={handleCloseSnackbar}
/>
)}
</>
);
};
export default ConsumeAction;

View File

@@ -0,0 +1,9 @@
const ConsumeDisplayComponent = ({ htmlContent }: { htmlContent: any }) => {
return (
<div>
<div dangerouslySetInnerHTML={{ __html: htmlContent }} />
</div>
);
};
export default ConsumeDisplayComponent;

View File

@@ -1,8 +1,19 @@
import { IEntityActions } from "@/types"; import { IEntityActions } from "@/types";
import { Button, Snackbar, Alert, AlertColor } from "@mui/material"; import {
Button,
Snackbar,
Alert,
AlertColor,
CircularProgress,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
} from "@mui/material";
import { useState } from "react"; import { useState } from "react";
import useAxios from "../hooks/useAxios";
import { deleteEntity } from "@/api/entities/entities"; import { deleteEntity } from "@/api/entities/entities";
import axios from "axios";
interface Props { interface Props {
endpointData: IEntityActions[]; endpointData: IEntityActions[];
@@ -16,25 +27,37 @@ const SNACKBAR_DEFAULT = {
}; };
const EntityActions = ({ endpointData, rowData }: Props) => { const EntityActions = ({ endpointData, rowData }: Props) => {
const [currentEndpoint, setCurrentEndpoint] = useState("");
const [shouldFetch, setShouldFetch] = useState(false);
const { error } = useAxios(currentEndpoint, "GET", null, true, shouldFetch);
const [snackbar, setSnackbar] = useState<{ const [snackbar, setSnackbar] = useState<{
open: boolean; open: boolean;
message: string; message: string;
severity: AlertColor; severity: AlertColor;
}>(SNACKBAR_DEFAULT); }>(SNACKBAR_DEFAULT);
console.error("Error registering/deregistering:", error); const [registerData, setRegisterData] = useState(null);
const [registerError, setRegisterError] = useState(null);
const [loadingRegister, setLoadingRegister] = useState(false);
const [DeregisterData, setDeRegisterData] = useState(null);
const [DeregisterError, setDeRegisterError] = useState(null);
const [loadingDeRegister, setLoadingDeRegister] = useState(false);
const [loadingDelete, setLoadingDelete] = useState(false);
const [confirmDelete, setConfirmDelete] = useState(false);
if (registerData) console.log("Register Data in state", registerData);
if (registerError) console.error("Register Error in state", registerError);
if (DeregisterData) console.log("Register Data in state", DeregisterData);
if (DeregisterError)
console.error("Register Error in state", DeregisterError);
const onDeleteEntity = async () => { const onDeleteEntity = async () => {
setLoadingDelete(true);
if (rowData) if (rowData)
try { try {
const response = await deleteEntity({ const response = await deleteEntity({
entity_did: rowData?.entity_did, entity_did: rowData?.entity_did,
}); });
console.log("On Delete:", response.data.message);
setSnackbar({ setSnackbar({
open: true, open: true,
message: response.data.message, message: response.data.message,
@@ -47,31 +70,109 @@ const EntityActions = ({ endpointData, rowData }: Props) => {
message: "Failed to delete entity.", message: "Failed to delete entity.",
severity: "error", severity: "error",
}); });
} finally {
setLoadingDelete(false);
closeDeleteConfirmation();
} }
}; };
const onRegisterEntity = (endpoint: string) => { const onRegisterEntity = (endpoint: string) => {
setCurrentEndpoint(endpoint); if (loadingRegister) return;
setShouldFetch(true);
setLoadingRegister(true);
const axiosConfig = {
url: endpoint,
method: "GET",
data: null,
withCredentials: true,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
};
axios(axiosConfig)
.then((response) => {
setRegisterData(response.data);
console.log("I got the data from register: ", response.data);
setSnackbar({
open: true,
message: "Registered successfully!",
severity: "success",
});
})
.catch((error) => {
console.error("Error happened during register: ", error);
setRegisterError(error);
setSnackbar({
open: true,
message: error,
severity: "error",
});
})
.finally(() => {
setLoadingRegister(false);
});
}; };
const onDeregisterEntity = (endpoint: string) => { const onDeregisterEntity = (endpoint: string) => {
setCurrentEndpoint(endpoint); if (loadingDeRegister) return;
setShouldFetch(true);
setLoadingDeRegister(true);
const axiosConfig = {
url: endpoint,
method: "GET",
data: null,
};
axios(axiosConfig)
.then((response) => {
setDeRegisterData(response.data);
console.log("I got the data from deregister: ", response.data);
setSnackbar({
open: true,
message: "De-Registered successfully!",
severity: "success",
});
})
.catch((error) => {
console.error("Error happened during deregister: ", error);
setDeRegisterError(error);
setSnackbar({
open: true,
message: error,
severity: "error",
});
})
.finally(() => {
setLoadingDeRegister(false);
});
}; };
const handleCloseSnackbar = () => { const handleCloseSnackbar = () => {
setSnackbar(SNACKBAR_DEFAULT); setSnackbar(SNACKBAR_DEFAULT);
}; };
const openDeleteConfirmation = () => {
setConfirmDelete(true);
};
const closeDeleteConfirmation = () => {
setConfirmDelete(false);
};
return ( return (
<> <>
<div className="flex justify-between"> <div className="flex justify-between">
{endpointData.map( {endpointData.map(
({ name, endpoint }: IEntityActions, index: number) => { ({ name, endpoint }: IEntityActions, index: number) => {
const isRegister = name && name.toLocaleLowerCase() === "register"; const isRegister = name && name.toLocaleLowerCase() === "register";
// const isDeRegister = name && name.toLocaleLowerCase() === "deregister";
return ( return (
<Button <Button
disabled={loadingRegister || loadingDeRegister}
key={index} key={index}
onClick={() => onClick={() =>
isRegister isRegister
@@ -86,10 +187,29 @@ const EntityActions = ({ endpointData, rowData }: Props) => {
); );
}, },
)} )}
<Button onClick={onDeleteEntity} size="small" variant="contained"> <Button
disabled={loadingDelete}
onClick={openDeleteConfirmation}
size="small"
variant="contained"
>
Delete Delete
</Button> </Button>
</div> </div>
<Dialog open={confirmDelete} onClose={closeDeleteConfirmation}>
<DialogTitle>Delete Entity Confirmation</DialogTitle>
<DialogContent>
<DialogContentText>
Are you sure you want to delete this entity?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={closeDeleteConfirmation}>Cancel</Button>
<Button variant="outlined" onClick={onDeleteEntity}>
{loadingDelete ? <CircularProgress size={24} /> : `Confirm`}
</Button>
</DialogActions>
</Dialog>
<Snackbar <Snackbar
anchorOrigin={{ vertical: "top", horizontal: "center" }} anchorOrigin={{ vertical: "top", horizontal: "center" }}
open={snackbar.open} open={snackbar.open}

View File

@@ -12,7 +12,13 @@ import { ICustomTable, CustomTableConfiguration } from "@/types";
import { Checkbox, Skeleton } from "@mui/material"; import { Checkbox, Skeleton } from "@mui/material";
import ErrorBoundary from "@/components/error_boundary"; import ErrorBoundary from "@/components/error_boundary";
const CustomTable = ({ configuration, data, loading, tkey }: ICustomTable) => { const CustomTable = ({
configuration,
data,
loading,
tkey,
onConsumeAction,
}: ICustomTable) => {
if (loading) if (loading)
return <Skeleton variant="rectangular" animation="wave" height={200} />; return <Skeleton variant="rectangular" animation="wave" height={200} />;
@@ -23,7 +29,11 @@ const CustomTable = ({ configuration, data, loading, tkey }: ICustomTable) => {
const renderTableCell = ( const renderTableCell = (
value: any, value: any,
cellKey: string, cellKey: string,
render?: (param: any, data?: any) => void | undefined, render?: (
param: any,
data?: any,
onFunc?: (param: any) => void,
) => void | undefined,
rowData?: any, rowData?: any,
) => { ) => {
let renderedValue = value; let renderedValue = value;
@@ -36,7 +46,7 @@ const CustomTable = ({ configuration, data, loading, tkey }: ICustomTable) => {
renderedValue = <Checkbox disabled checked={value} />; renderedValue = <Checkbox disabled checked={value} />;
// cover use case if we want to render a component // cover use case if we want to render a component
if (render) renderedValue = render(value, rowData); if (render) renderedValue = render(value, rowData, onConsumeAction);
// catch use case where the value is an object but the render function is not provided in the table config // catch use case where the value is an object but the render function is not provided in the table config
if ( if (

View File

@@ -1,5 +1,5 @@
import { Button } from "@mui/material";
import EntityActions from "@/components/entity_actions"; import EntityActions from "@/components/entity_actions";
import ConsumeAction from "@/components/consume_action";
export const ClientTableConfig = [ export const ClientTableConfig = [
{ {
@@ -13,11 +13,13 @@ export const ClientTableConfig = [
{ {
key: "endpoint_url", key: "endpoint_url",
label: "End Point", label: "End Point",
render: () => { render: (value: any, rowData: any, onConsume: any) => {
return ( return (
<Button disabled variant="outlined"> <ConsumeAction
Consume rowData={rowData}
</Button> onConsume={onConsume}
endpoint={value}
/>
); );
}, },
}, },

View File

@@ -1,7 +1,7 @@
export interface CustomTableConfiguration { export interface CustomTableConfiguration {
key: string; key: string;
label: string; label: string;
render?: (param: any) => void; render?: (param: any, rowData?: any, onConsume?: any) => void;
} }
export interface ICustomTable { export interface ICustomTable {
@@ -9,6 +9,7 @@ export interface ICustomTable {
data: any; data: any;
loading?: boolean; loading?: boolean;
tkey: string; tkey: string;
onConsumeAction?: (param: any) => void;
} }
export interface EntityDetails { export interface EntityDetails {