Moved notifications lambda pages to settings (#496)
This commit is contained in:
@@ -28,7 +28,6 @@ var (
|
||||
iamPolicies = "/policies"
|
||||
dashboard = "/dashboard"
|
||||
profiling = "/profiling"
|
||||
notifications = "/notification-endpoints"
|
||||
buckets = "/buckets"
|
||||
bucketsDetail = "/buckets/:bucketName"
|
||||
serviceAccounts = "/account"
|
||||
@@ -124,18 +123,6 @@ var usersActionSet = ConfigurationActionSet{
|
||||
),
|
||||
}
|
||||
|
||||
// notificationsActionSet contains the list of admin actions required for this endpoint to work
|
||||
var notificationsActionSet = ConfigurationActionSet{
|
||||
actionTypes: iampolicy.NewActionSet(
|
||||
iampolicy.AllActions,
|
||||
),
|
||||
actions: iampolicy.NewActionSet(
|
||||
iampolicy.ListenBucketNotificationAction,
|
||||
iampolicy.PutBucketNotificationAction,
|
||||
iampolicy.GetBucketNotificationAction,
|
||||
),
|
||||
}
|
||||
|
||||
// bucketsActionSet contains the list of admin actions required for this endpoint to work
|
||||
var bucketsActionSet = ConfigurationActionSet{
|
||||
actionTypes: iampolicy.NewActionSet(
|
||||
@@ -252,7 +239,6 @@ var endpointRules = map[string]ConfigurationActionSet{
|
||||
iamPolicies: iamPoliciesActionSet,
|
||||
dashboard: dashboardActionSet,
|
||||
profiling: profilingActionSet,
|
||||
notifications: notificationsActionSet,
|
||||
buckets: bucketsActionSet,
|
||||
bucketsDetail: bucketsActionSet,
|
||||
serviceAccounts: serviceAccountsActionSet,
|
||||
|
||||
@@ -81,7 +81,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"s3:*",
|
||||
},
|
||||
},
|
||||
want: 8,
|
||||
want: 7,
|
||||
},
|
||||
{
|
||||
name: "all admin and s3 endpoints",
|
||||
@@ -91,7 +91,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"s3:*",
|
||||
},
|
||||
},
|
||||
want: 20,
|
||||
want: 19,
|
||||
},
|
||||
{
|
||||
name: "Console User - default endpoints",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -263,4 +263,32 @@ export const settingsCommon = {
|
||||
padding: "15px 38px",
|
||||
textAlign: "right" as const,
|
||||
},
|
||||
settingsOptionsContainer: {
|
||||
height: "calc(100vh - 244px)",
|
||||
backgroundColor: "#fff",
|
||||
border: "#EAEDEE 1px solid",
|
||||
borderRadius: 3,
|
||||
marginTop: 15,
|
||||
},
|
||||
backButton: {
|
||||
cursor: "pointer",
|
||||
fontSize: 10,
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
backgroundColor: "transparent",
|
||||
border: 0,
|
||||
padding: 0,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
"&:active, &:focus": {
|
||||
outline: 0,
|
||||
},
|
||||
"& svg": {
|
||||
width: 10,
|
||||
marginRight: 4,
|
||||
},
|
||||
},
|
||||
backContainer: {
|
||||
margin: "20px 38px 0",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -48,7 +48,6 @@ const SlideOptions = ({
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({ width, height }: any) => {
|
||||
console.log(width, height);
|
||||
const currentSliderPosition = currentSlide * width;
|
||||
const containerSize = width * slideOptions.length;
|
||||
return (
|
||||
@@ -61,9 +60,13 @@ const SlideOptions = ({
|
||||
width: `${containerSize}px`,
|
||||
}}
|
||||
>
|
||||
{slideOptions.map((block: any) => {
|
||||
{slideOptions.map((block: any, index: number) => {
|
||||
return (
|
||||
<div className={classes.slide} style={{ width }}>
|
||||
<div
|
||||
className={classes.slide}
|
||||
style={{ width }}
|
||||
key={`slide-panel-${index.toString()}`}
|
||||
>
|
||||
{block}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { containerForHeader } from "../Common/FormComponents/common/styleLibrary
|
||||
import Tab from "@material-ui/core/Tab";
|
||||
import Tabs from "@material-ui/core/Tabs";
|
||||
import ConfigurationsList from "./ConfigurationPanels/ConfigurationsList";
|
||||
import ListNotificationEndpoints from "./NotificationEndpoints/ListNotificationEndpoints";
|
||||
|
||||
interface IConfigurationMain {
|
||||
classes: any;
|
||||
@@ -50,7 +51,11 @@ const ConfigurationMain = ({ classes }: IConfigurationMain) => {
|
||||
<ConfigurationsList />
|
||||
</Grid>
|
||||
)}
|
||||
{selectedTab === 1 && <div>Lambda notifications</div>}
|
||||
{selectedTab === 1 && (
|
||||
<Grid item xs={12}>
|
||||
<ListNotificationEndpoints />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -18,9 +18,6 @@ import React, { useState, Fragment } from "react";
|
||||
import get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { TextField } from "@material-ui/core";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import history from "../../../../history";
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import { configurationElements } from "../utils";
|
||||
@@ -60,34 +57,6 @@ const styles = (theme: Theme) =>
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
settingsOptionsContainer: {
|
||||
height: "calc(100vh - 244px)",
|
||||
backgroundColor: "#fff",
|
||||
border: "#EAEDEE 1px solid",
|
||||
borderRadius: 3,
|
||||
marginTop: 15,
|
||||
},
|
||||
backButton: {
|
||||
cursor: "pointer",
|
||||
fontSize: 10,
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
backgroundColor: "transparent",
|
||||
border: 0,
|
||||
padding: 0,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
"&:active, &:focus": {
|
||||
outline: 0,
|
||||
},
|
||||
"& svg": {
|
||||
width: 10,
|
||||
marginRight: 4,
|
||||
},
|
||||
},
|
||||
backContainer: {
|
||||
margin: "20px 38px 0",
|
||||
},
|
||||
...searchField,
|
||||
...actionsTray,
|
||||
...settingsCommon,
|
||||
@@ -136,7 +105,7 @@ const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Grid item xs={12}>
|
||||
@@ -191,7 +160,7 @@ const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ const ConfMySql = ({ onChange, classes }: IConfMySqlProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid container className={classes.formScrollable}>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
label={"Enter DNS String"}
|
||||
|
||||
@@ -202,7 +202,7 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
||||
}, [useConnectionString]);
|
||||
|
||||
return (
|
||||
<Grid container className={classes.formScrollable}>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
label={"Manually Configure String"}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useCallback, useEffect, useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button } from "@material-ui/core";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import ConfPostgres from "../CustomForms/ConfPostgres";
|
||||
import api from "../../../../common/api";
|
||||
import { serverNeedsRestart } from "../../../../actions";
|
||||
import { connect } from "react-redux";
|
||||
import ConfMySql from "../CustomForms/ConfMySql";
|
||||
import ConfTargetGeneric from "../ConfTargetGeneric";
|
||||
import {
|
||||
notificationEndpointsFields,
|
||||
notifyPostgres,
|
||||
notifyMysql,
|
||||
removeEmptyFields,
|
||||
} from "../utils";
|
||||
import { IElementValue } from "../types";
|
||||
import {
|
||||
modalBasic,
|
||||
settingsCommon,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { servicesList } from "./utils";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...modalBasic,
|
||||
...settingsCommon,
|
||||
errorBlock: {
|
||||
color: "red",
|
||||
},
|
||||
strongText: {
|
||||
fontWeight: 700,
|
||||
},
|
||||
keyName: {
|
||||
marginLeft: 5,
|
||||
},
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
lambdaFormIndicator: {
|
||||
display: "flex",
|
||||
marginBottom: 40,
|
||||
},
|
||||
customTitle: {
|
||||
...settingsCommon.customTitle,
|
||||
marginTop: 0,
|
||||
},
|
||||
settingsFormContainer: {
|
||||
...settingsCommon.settingsFormContainer,
|
||||
height: "calc(100vh - 422px)",
|
||||
},
|
||||
});
|
||||
|
||||
interface IAddNotificationEndpointProps {
|
||||
service: string;
|
||||
saveAndRefresh: any;
|
||||
serverNeedsRestart: typeof serverNeedsRestart;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const AddNotificationEndpoint = ({
|
||||
service,
|
||||
saveAndRefresh,
|
||||
serverNeedsRestart,
|
||||
classes,
|
||||
}: IAddNotificationEndpointProps) => {
|
||||
//Local States
|
||||
const [valuesArr, setValueArr] = useState<IElementValue[]>([]);
|
||||
const [saving, setSaving] = useState<boolean>(false);
|
||||
const [addError, setError] = useState<string>("");
|
||||
|
||||
//Effects
|
||||
|
||||
useEffect(() => {
|
||||
if (saving) {
|
||||
const payload = {
|
||||
key_values: removeEmptyFields(valuesArr),
|
||||
};
|
||||
api
|
||||
.invoke("PUT", `/api/v1/configs/${service}`, payload)
|
||||
.then(() => {
|
||||
setSaving(false);
|
||||
setError("");
|
||||
serverNeedsRestart(true);
|
||||
saveAndRefresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
setSaving(false);
|
||||
setError(err);
|
||||
});
|
||||
}
|
||||
}, [saving, serverNeedsRestart, service, valuesArr, saveAndRefresh]);
|
||||
|
||||
//Fetch Actions
|
||||
const submitForm = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
setSaving(true);
|
||||
};
|
||||
|
||||
const onValueChange = useCallback(
|
||||
(newValue) => {
|
||||
setValueArr(newValue);
|
||||
},
|
||||
[setValueArr]
|
||||
);
|
||||
|
||||
let srvComponent;
|
||||
switch (service) {
|
||||
case notifyPostgres: {
|
||||
srvComponent = <ConfPostgres onChange={onValueChange} />;
|
||||
break;
|
||||
}
|
||||
case notifyMysql: {
|
||||
srvComponent = <ConfMySql onChange={onValueChange} />;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const fields = get(notificationEndpointsFields, service, []);
|
||||
|
||||
srvComponent = (
|
||||
<ConfTargetGeneric fields={fields} onChange={onValueChange} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const targetElement = servicesList.find(
|
||||
(element) => element.actionTrigger === service
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{service !== "" && (
|
||||
<Fragment>
|
||||
<form noValidate onSubmit={submitForm}>
|
||||
<Grid item xs={12} className={classes.customTitle}>
|
||||
{targetElement ? targetElement.targetTitle : ""} - Add Lambda
|
||||
Notification Target
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.settingsFormContainer}>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
{srvComponent}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.settingsButtonContainer}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={saving}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9} />
|
||||
</form>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const connector = connect(null, { serverNeedsRestart });
|
||||
|
||||
export default connector(withStyles(styles)(AddNotificationEndpoint));
|
||||
@@ -0,0 +1,289 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useState, Fragment } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { TextField } from "@material-ui/core";
|
||||
import { red } from "@material-ui/core/colors";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import {
|
||||
NotificationEndpointItem,
|
||||
NotificationEndpointsList,
|
||||
TransformedEndpointItem,
|
||||
} from "./types";
|
||||
import { notificationTransform } from "./utils";
|
||||
import { CreateIcon } from "../../../../icons";
|
||||
import api from "../../../../common/api";
|
||||
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import AddNotificationEndpoint from "./AddNotificationEndpoint";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
settingsCommon,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import SlideOptions from "../../Common/SlideOptions/SlideOptions";
|
||||
import BackSettingsIcon from "../../../../icons/BackSettingsIcon";
|
||||
import NotificationTypeSelector from "./NotificationTypeSelector";
|
||||
|
||||
interface IListNotificationEndpoints {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...settingsCommon,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
errorBlock: {
|
||||
color: "red",
|
||||
},
|
||||
strongText: {
|
||||
fontWeight: 700,
|
||||
},
|
||||
keyName: {
|
||||
marginLeft: 5,
|
||||
},
|
||||
iconText: {
|
||||
lineHeight: "24px",
|
||||
},
|
||||
customConfigurationPage: {
|
||||
height: "calc(100vh - 410px)",
|
||||
scrollbarWidth: "none" as const,
|
||||
"&::-webkit-scrollbar": {
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
lambdaContainer: {
|
||||
padding: "15px 0",
|
||||
},
|
||||
actionsTray: {
|
||||
...actionsTray.actionsTray,
|
||||
padding: "0 38px",
|
||||
},
|
||||
});
|
||||
|
||||
const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
||||
//Local States
|
||||
const [records, setRecords] = useState<TransformedEndpointItem[]>([]);
|
||||
const [filter, setFilter] = useState<string>("");
|
||||
const [error, setError] = useState<string>("");
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [currentPanel, setCurrentPanel] = useState<number>(0);
|
||||
const [service, setService] = useState<string>("");
|
||||
|
||||
//Effects
|
||||
// load records on mount
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
const fetchRecords = () => {
|
||||
api
|
||||
.invoke("GET", `/api/v1/admin/notification_endpoints`)
|
||||
.then((res: NotificationEndpointsList) => {
|
||||
let resNotEndList: NotificationEndpointItem[] = [];
|
||||
if (res.notification_endpoints !== null) {
|
||||
resNotEndList = res.notification_endpoints;
|
||||
}
|
||||
setRecords(notificationTransform(resNotEndList));
|
||||
setError("");
|
||||
setIsLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
setError(err);
|
||||
setIsLoading(false);
|
||||
});
|
||||
};
|
||||
fetchRecords();
|
||||
}
|
||||
}, [isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
}, []);
|
||||
|
||||
const tableActions = [
|
||||
{
|
||||
type: "delete",
|
||||
onClick: (row: any) => {
|
||||
//confirmDeleteBucket(row.name);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const filteredRecords = records.filter((b: TransformedEndpointItem) => {
|
||||
if (filter === "") {
|
||||
return true;
|
||||
} else {
|
||||
if (b.service_name.indexOf(filter) >= 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const statusDisplay = (status: string) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<FiberManualRecordIcon
|
||||
style={status === "Offline" ? { color: red[500] } : {}}
|
||||
/>
|
||||
{status}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const openNewLambdaSelector = () => {
|
||||
setCurrentPanel(1);
|
||||
};
|
||||
|
||||
const backClick = () => {
|
||||
setService("");
|
||||
setCurrentPanel(currentPanel - 1);
|
||||
};
|
||||
|
||||
const saveAndRefresh = () => {
|
||||
setIsLoading(true);
|
||||
setCurrentPanel(0);
|
||||
setService("");
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.settingsOptionsContainer}>
|
||||
<SlideOptions
|
||||
slideOptions={[
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.customTitle}>
|
||||
Lambda Notification Targets
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} className={classes.lambdaContainer}>
|
||||
{error !== "" && <Grid container>{error}</Grid>}
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Filter"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(event) => {
|
||||
setFilter(event.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={openNewLambdaSelector}
|
||||
>
|
||||
Add Notification Target
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "status",
|
||||
renderFunction: statusDisplay,
|
||||
width: 150,
|
||||
},
|
||||
{ label: "Service", elementKey: "service_name" },
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Notification Endpoints"
|
||||
idField="service_name"
|
||||
customPaperHeight={classes.customConfigurationPage}
|
||||
noBackground
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>,
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.backContainer}>
|
||||
<button
|
||||
onClick={backClick}
|
||||
className={classes.backButton}
|
||||
>
|
||||
<BackSettingsIcon />
|
||||
Back To Lambda Notifications
|
||||
</button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<NotificationTypeSelector
|
||||
setService={(serviceName: string) => {
|
||||
setService(serviceName);
|
||||
setCurrentPanel(2);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>,
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.backContainer}>
|
||||
<button
|
||||
onClick={backClick}
|
||||
className={classes.backButton}
|
||||
>
|
||||
<BackSettingsIcon />
|
||||
Back To Supported Services
|
||||
</button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<AddNotificationEndpoint
|
||||
service={service}
|
||||
saveAndRefresh={saveAndRefresh}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>,
|
||||
]}
|
||||
currentSlide={currentPanel}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(ListNotificationEndpoints);
|
||||
@@ -0,0 +1,141 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { Button } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { servicesList } from "./utils";
|
||||
import { settingsCommon } from "../../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
interface INotificationTypeSelector {
|
||||
classes: any;
|
||||
setService: (trigger: string) => any;
|
||||
}
|
||||
|
||||
const nonLogos = servicesList.filter((elService) => elService.logo === "");
|
||||
const withLogos = servicesList.filter((elService) => elService.logo !== "");
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...settingsCommon,
|
||||
logoButton: {
|
||||
height: "80px",
|
||||
},
|
||||
lambdaNotif: {
|
||||
backgroundColor: "#fff",
|
||||
border: "#393939 1px solid",
|
||||
borderRadius: 5,
|
||||
width: 101,
|
||||
height: 91,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginBottom: 16,
|
||||
cursor: "pointer",
|
||||
"& img": {
|
||||
maxWidth: 71,
|
||||
maxHeight: 71,
|
||||
},
|
||||
},
|
||||
iconContainer: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
maxWidth: 455,
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
},
|
||||
nonIconContainer: {
|
||||
marginBottom: 16,
|
||||
width: 455,
|
||||
marginTop: 15,
|
||||
"& button": {
|
||||
marginRight: 16,
|
||||
},
|
||||
},
|
||||
pickTitle: {
|
||||
fontWeight: 600,
|
||||
color: "#393939",
|
||||
fontSize: 14,
|
||||
marginBottom: 16,
|
||||
},
|
||||
centerElements: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
customTitle: {
|
||||
...settingsCommon.customTitle,
|
||||
marginTop: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const NotificationTypeSelector = ({
|
||||
classes,
|
||||
setService,
|
||||
}: INotificationTypeSelector) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Grid item xs={12} className={classes.customTitle}>
|
||||
Pick a supported service
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.centerElements}>
|
||||
<div className={classes.nonIconContainer}>
|
||||
{nonLogos.map((item) => {
|
||||
return (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
key={`non-icon-${item.targetTitle}`}
|
||||
onClick={() => {
|
||||
setService(item.actionTrigger);
|
||||
}}
|
||||
>
|
||||
{item.targetTitle.toUpperCase()}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className={classes.iconContainer}>
|
||||
{withLogos.map((item) => {
|
||||
return (
|
||||
<button
|
||||
key={`icon-${item.targetTitle}`}
|
||||
className={classes.lambdaNotif}
|
||||
onClick={() => {
|
||||
setService(item.actionTrigger);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={item.logo}
|
||||
className={classes.logoButton}
|
||||
alt={item.targetTitle}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(NotificationTypeSelector);
|
||||
@@ -0,0 +1,93 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { NotificationEndpointItem } from "./types";
|
||||
import {
|
||||
notifyAmqp,
|
||||
notifyElasticsearch,
|
||||
notifyKafka,
|
||||
notifyMqtt,
|
||||
notifyMysql,
|
||||
notifyNats,
|
||||
notifyNsq,
|
||||
notifyPostgres,
|
||||
notifyRedis,
|
||||
notifyWebhooks,
|
||||
} from "../utils";
|
||||
|
||||
export const notificationTransform = (
|
||||
notificationElements: NotificationEndpointItem[]
|
||||
) => {
|
||||
return notificationElements.map((element) => {
|
||||
return {
|
||||
service_name: `${element.service}:${element.account_id}`,
|
||||
status: element.status,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const servicesList = [
|
||||
{
|
||||
actionTrigger: notifyPostgres,
|
||||
targetTitle: "Postgres SQL",
|
||||
logo: "/postgres.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyKafka,
|
||||
targetTitle: "Kafka",
|
||||
logo: "/kafka.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyAmqp,
|
||||
targetTitle: "AMQP",
|
||||
logo: "/amqp.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyMqtt,
|
||||
targetTitle: "MQTT",
|
||||
logo: "/mqtt.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyRedis,
|
||||
targetTitle: "Redis",
|
||||
logo: "/redis.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyNats,
|
||||
targetTitle: "NATS",
|
||||
logo: "/nats.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyMysql,
|
||||
targetTitle: "Mysql",
|
||||
logo: "/mysql.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyElasticsearch,
|
||||
targetTitle: "Elastic Search",
|
||||
logo: "/elasticsearch.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyWebhooks,
|
||||
targetTitle: "Webhook",
|
||||
logo: "",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyNsq,
|
||||
targetTitle: "NSQ",
|
||||
logo: "",
|
||||
},
|
||||
];
|
||||
@@ -39,7 +39,7 @@ import api from "../../common/api";
|
||||
import Account from "./Account/Account";
|
||||
import Users from "./Users/Users";
|
||||
import Groups from "./Groups/Groups";
|
||||
import ListNotificationEndpoints from "./NotificationEndopoints/ListNotificationEndpoints";
|
||||
import ListNotificationEndpoints from "./Configurations/NotificationEndpoints/ListNotificationEndpoints";
|
||||
import ConfigurationMain from "./Configurations/ConfigurationMain";
|
||||
import WebhookPanel from "./Configurations/ConfigurationPanels/WebhookPanel";
|
||||
import ListTenants from "./Tenants/ListTenants/ListTenants";
|
||||
@@ -248,10 +248,6 @@ const Console = ({
|
||||
component: Logs,
|
||||
path: "/logs",
|
||||
},
|
||||
{
|
||||
component: ListNotificationEndpoints,
|
||||
path: "/notification-endpoints",
|
||||
},
|
||||
{
|
||||
component: ConfigurationMain,
|
||||
path: "/settings",
|
||||
|
||||
@@ -274,14 +274,6 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
||||
name: "Heal",
|
||||
icon: <HealIcon />,
|
||||
},
|
||||
{
|
||||
group: "Admin",
|
||||
type: "item",
|
||||
component: NavLink,
|
||||
to: "/notification-endpoints",
|
||||
name: "Lambda Notifications",
|
||||
icon: <LambdaNotificationsIcon />,
|
||||
},
|
||||
{
|
||||
group: "Admin",
|
||||
type: "item",
|
||||
|
||||
@@ -1,385 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2019 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import ConfPostgres from "../Configurations/CustomForms/ConfPostgres";
|
||||
import api from "../../../common/api";
|
||||
import { serverNeedsRestart } from "../../../actions";
|
||||
import { connect } from "react-redux";
|
||||
import ConfMySql from "../Configurations/CustomForms/ConfMySql";
|
||||
import ConfTargetGeneric from "../Configurations/ConfTargetGeneric";
|
||||
import {
|
||||
notificationEndpointsFields,
|
||||
notifyPostgres,
|
||||
notifyMysql,
|
||||
notifyKafka,
|
||||
notifyAmqp,
|
||||
notifyMqtt,
|
||||
notifyRedis,
|
||||
notifyNats,
|
||||
notifyElasticsearch,
|
||||
notifyWebhooks,
|
||||
notifyNsq,
|
||||
removeEmptyFields,
|
||||
} from "../Configurations/utils";
|
||||
import { IElementValue } from "../Configurations/types";
|
||||
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red",
|
||||
},
|
||||
strongText: {
|
||||
fontWeight: 700,
|
||||
},
|
||||
keyName: {
|
||||
marginLeft: 5,
|
||||
},
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
logoButton: {
|
||||
height: "80px",
|
||||
},
|
||||
lambdaNotif: {
|
||||
backgroundColor: "#fff",
|
||||
border: "#393939 1px solid",
|
||||
borderRadius: 5,
|
||||
width: 101,
|
||||
height: 91,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginBottom: 16,
|
||||
cursor: "pointer",
|
||||
"& img": {
|
||||
maxWidth: 71,
|
||||
maxHeight: 71,
|
||||
},
|
||||
},
|
||||
iconContainer: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
width: 455,
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
},
|
||||
nonIconContainer: {
|
||||
marginBottom: 16,
|
||||
"& button": {
|
||||
marginRight: 16,
|
||||
},
|
||||
},
|
||||
pickTitle: {
|
||||
fontWeight: 600,
|
||||
color: "#393939",
|
||||
fontSize: 14,
|
||||
marginBottom: 16,
|
||||
},
|
||||
lambdaFormIndicator: {
|
||||
display: "flex",
|
||||
marginBottom: 40,
|
||||
},
|
||||
lambdaName: {
|
||||
fontSize: 18,
|
||||
fontWeight: 700,
|
||||
color: "#000",
|
||||
marginBottom: 6,
|
||||
},
|
||||
lambdaSubname: {
|
||||
fontSize: 12,
|
||||
color: "#000",
|
||||
fontWeight: 600,
|
||||
},
|
||||
lambdaIcon: {
|
||||
borderRadius: 5,
|
||||
border: "#393939 1px solid",
|
||||
width: 53,
|
||||
height: 48,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: 16,
|
||||
"& img": {
|
||||
width: 38,
|
||||
},
|
||||
},
|
||||
...modalBasic,
|
||||
});
|
||||
|
||||
interface IAddNotificationEndpointProps {
|
||||
open: boolean;
|
||||
closeModalAndRefresh: any;
|
||||
serverNeedsRestart: typeof serverNeedsRestart;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const AddNotificationEndpoint = ({
|
||||
open,
|
||||
closeModalAndRefresh,
|
||||
serverNeedsRestart,
|
||||
classes,
|
||||
}: IAddNotificationEndpointProps) => {
|
||||
//Local States
|
||||
const [service, setService] = useState<string>("");
|
||||
const [valuesArr, setValueArr] = useState<IElementValue[]>([]);
|
||||
const [saving, setSaving] = useState<boolean>(false);
|
||||
const [addError, setError] = useState<string>("");
|
||||
|
||||
//Effects
|
||||
|
||||
useEffect(() => {
|
||||
if (saving) {
|
||||
const payload = {
|
||||
key_values: removeEmptyFields(valuesArr),
|
||||
};
|
||||
api
|
||||
.invoke("PUT", `/api/v1/configs/${service}`, payload)
|
||||
.then((res) => {
|
||||
setSaving(false);
|
||||
setError("");
|
||||
serverNeedsRestart(true);
|
||||
|
||||
closeModalAndRefresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
setSaving(false);
|
||||
setError(err);
|
||||
});
|
||||
}
|
||||
}, [saving, serverNeedsRestart, service, valuesArr, closeModalAndRefresh]);
|
||||
|
||||
//Fetch Actions
|
||||
const submitForm = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
setSaving(true);
|
||||
};
|
||||
|
||||
const onValueChange = useCallback(
|
||||
(newValue) => {
|
||||
setValueArr(newValue);
|
||||
},
|
||||
[setValueArr]
|
||||
);
|
||||
|
||||
let srvComponent = <React.Fragment />;
|
||||
switch (service) {
|
||||
case notifyPostgres: {
|
||||
srvComponent = <ConfPostgres onChange={onValueChange} />;
|
||||
break;
|
||||
}
|
||||
case notifyMysql: {
|
||||
srvComponent = <ConfMySql onChange={onValueChange} />;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const fields = get(notificationEndpointsFields, service, []);
|
||||
|
||||
srvComponent = (
|
||||
<ConfTargetGeneric fields={fields} onChange={onValueChange} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const servicesList = [
|
||||
{
|
||||
actionTrigger: notifyPostgres,
|
||||
targetTitle: "Postgres SQL",
|
||||
logo: "/postgres.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyKafka,
|
||||
targetTitle: "Kafka",
|
||||
logo: "/kafka.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyAmqp,
|
||||
targetTitle: "AMQP",
|
||||
logo: "/amqp.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyMqtt,
|
||||
targetTitle: "MQTT",
|
||||
logo: "/mqtt.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyRedis,
|
||||
targetTitle: "Redis",
|
||||
logo: "/redis.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyNats,
|
||||
targetTitle: "NATS",
|
||||
logo: "/nats.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyMysql,
|
||||
targetTitle: "Mysql",
|
||||
logo: "/mysql.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyElasticsearch,
|
||||
targetTitle: "Elastic Search",
|
||||
logo: "/elasticsearch.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyWebhooks,
|
||||
targetTitle: "Webhook",
|
||||
logo: "",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyNsq,
|
||||
targetTitle: "NSQ",
|
||||
logo: "",
|
||||
},
|
||||
];
|
||||
|
||||
const nonLogos = servicesList.filter((elService) => elService.logo === "");
|
||||
const withLogos = servicesList.filter((elService) => elService.logo !== "");
|
||||
|
||||
const targetElement = servicesList.find(
|
||||
(element) => element.actionTrigger === service
|
||||
);
|
||||
|
||||
const goBack = () => {
|
||||
setService("");
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalWrapper modalOpen={open} onClose={closeModalAndRefresh} title={""}>
|
||||
{service === "" && (
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.pickTitle}>Pick a supported service:</div>
|
||||
<div className={classes.nonIconContainer}>
|
||||
{nonLogos.map((item) => {
|
||||
return (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
key={`non-icon-${item.targetTitle}`}
|
||||
onClick={() => {
|
||||
setService(item.actionTrigger);
|
||||
}}
|
||||
>
|
||||
{item.targetTitle.toUpperCase()}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className={classes.iconContainer}>
|
||||
{withLogos.map((item) => {
|
||||
return (
|
||||
<button
|
||||
key={`icon-${item.targetTitle}`}
|
||||
className={classes.lambdaNotif}
|
||||
onClick={() => {
|
||||
setService(item.actionTrigger);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={item.logo}
|
||||
className={classes.logoButton}
|
||||
alt={item.targetTitle}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
|
||||
{saving && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
)}
|
||||
{service !== "" && (
|
||||
<React.Fragment>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<form noValidate onSubmit={submitForm}>
|
||||
<Grid item xs={12} className={classes.lambdaFormIndicator}>
|
||||
{targetElement && targetElement.logo !== "" && (
|
||||
<div className={classes.lambdaIcon}>
|
||||
<img
|
||||
src={targetElement.logo}
|
||||
alt={targetElement.targetTitle}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={classes.lambdaTitle}>
|
||||
<div className={classes.lambdaName}>
|
||||
{targetElement ? targetElement.targetTitle : ""}
|
||||
</div>
|
||||
<div className={classes.lambdaSubname}>
|
||||
Add Lambda Notification Target
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{srvComponent}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={goBack}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={saving}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={9} />
|
||||
</form>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const connector = connect(null, { serverNeedsRestart });
|
||||
|
||||
export default connector(withStyles(styles)(AddNotificationEndpoint));
|
||||
@@ -1,211 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { TextField } from "@material-ui/core";
|
||||
import { red } from "@material-ui/core/colors";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import {
|
||||
NotificationEndpointItem,
|
||||
NotificationEndpointsList,
|
||||
TransformedEndpointItem,
|
||||
} from "./types";
|
||||
import { notificationTransform } from "./utils";
|
||||
import { CreateIcon } from "../../../icons";
|
||||
import api from "../../../common/api";
|
||||
import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import AddNotificationEndpoint from "./AddNotificationEndpoint";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
|
||||
interface IListNotificationEndpoints {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red",
|
||||
},
|
||||
strongText: {
|
||||
fontWeight: 700,
|
||||
},
|
||||
keyName: {
|
||||
marginLeft: 5,
|
||||
},
|
||||
iconText: {
|
||||
lineHeight: "24px",
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
||||
//Local States
|
||||
const [records, setRecords] = useState<TransformedEndpointItem[]>([]);
|
||||
const [filter, setFilter] = useState<string>("");
|
||||
const [error, setError] = useState<string>("");
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||
|
||||
//Effects
|
||||
// load records on mount
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
const fetchRecords = () => {
|
||||
api
|
||||
.invoke("GET", `/api/v1/admin/notification_endpoints`)
|
||||
.then((res: NotificationEndpointsList) => {
|
||||
let resNotEndList: NotificationEndpointItem[] = [];
|
||||
if (res.notification_endpoints !== null) {
|
||||
resNotEndList = res.notification_endpoints;
|
||||
}
|
||||
setRecords(notificationTransform(resNotEndList));
|
||||
setError("");
|
||||
setIsLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
setError(err);
|
||||
setIsLoading(false);
|
||||
});
|
||||
};
|
||||
fetchRecords();
|
||||
}
|
||||
}, [isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
}, []);
|
||||
|
||||
const tableActions = [
|
||||
{ type: "view", to: "/notification-endpoints", sendOnlyId: true },
|
||||
{
|
||||
type: "delete",
|
||||
onClick: (row: any) => {
|
||||
//confirmDeleteBucket(row.name);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const filteredRecords = records.filter((b: TransformedEndpointItem) => {
|
||||
if (filter === "") {
|
||||
return true;
|
||||
} else {
|
||||
if (b.service_name.indexOf(filter) >= 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const statusDisplay = (status: string) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<FiberManualRecordIcon
|
||||
style={status === "Offline" ? { color: red[500] } : {}}
|
||||
/>
|
||||
{status}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{addScreenOpen && (
|
||||
<AddNotificationEndpoint
|
||||
open={addScreenOpen}
|
||||
closeModalAndRefresh={() => {
|
||||
setIsLoading(true);
|
||||
setAddScreenOpen(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<PageHeader label="Lambda Notification Targets" />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
{error !== "" && <Grid container>{error}</Grid>}
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Filter"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(event) => {
|
||||
setFilter(event.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Notification Target
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "Service", elementKey: "service_name" },
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "status",
|
||||
renderFunction: statusDisplay,
|
||||
},
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Notification Endpoints"
|
||||
idField="service_name"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(ListNotificationEndpoints);
|
||||
@@ -1,28 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { NotificationEndpointItem } from "./types";
|
||||
|
||||
export const notificationTransform = (
|
||||
notificationElements: NotificationEndpointItem[]
|
||||
) => {
|
||||
return notificationElements.map(element => {
|
||||
return {
|
||||
service_name: `${element.service}:${element.account_id}`,
|
||||
status: element.status
|
||||
};
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user