Fixed Bucket Events Notifications Page (#2906)

- Added missing ilm & replica supported types
- Fixed add screen crash when no arn is selected
- Migrated components to mds

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2023-06-28 15:39:02 -06:00
committed by GitHub
parent d1ae271111
commit df937467a0
10 changed files with 92 additions and 142 deletions

2
go.mod
View File

@@ -23,7 +23,7 @@ require (
github.com/minio/kes v0.22.3
github.com/minio/madmin-go/v3 v3.0.2
github.com/minio/mc v0.0.0-20230619193119-5f39522e6902
github.com/minio/minio-go/v7 v7.0.55-0.20230525060734-b7836f021bfb
github.com/minio/minio-go/v7 v7.0.58-0.20230622175401-7048a16cfbca
github.com/minio/pkg v1.7.5
github.com/minio/selfupdate v0.6.0
github.com/minio/websocket v1.6.0

4
go.sum
View File

@@ -254,8 +254,8 @@ github.com/minio/mc v0.0.0-20230619193119-5f39522e6902 h1:oNewsjSewAfS4Bly8E0cqQ
github.com/minio/mc v0.0.0-20230619193119-5f39522e6902/go.mod h1:DZvNh8trMmDzTcxI2WVQeD2f5yd2UDDk9RYsFxaOMaQ=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.55-0.20230525060734-b7836f021bfb h1:oW9owq24i06IMYrfufzdjLH5S4rcOc9M1f7Cak+Ya5I=
github.com/minio/minio-go/v7 v7.0.55-0.20230525060734-b7836f021bfb/go.mod h1:NUDy4A4oXPq1l2yK6LTSvCEzAMeIcoz9lcj5dbzSrRE=
github.com/minio/minio-go/v7 v7.0.58-0.20230622175401-7048a16cfbca h1:/cqQl5kUAeTVVfTiJTWN0r4USgUBhjqfwamlnvBqW9k=
github.com/minio/minio-go/v7 v7.0.58-0.20230622175401-7048a16cfbca/go.mod h1:NUDy4A4oXPq1l2yK6LTSvCEzAMeIcoz9lcj5dbzSrRE=
github.com/minio/mux v1.9.0 h1:dWafQFyEfGhJvK6AwLOt83bIG5bxKxKJnKMCi0XAaoA=
github.com/minio/pkg v1.7.5 h1:UOUJjewE5zoaDPlCMJtNx/swc1jT1ZR+IajT7hrLd44=
github.com/minio/pkg v1.7.5/go.mod h1:mEfGMTm5Z0b5EGxKNuPwyb5A2d+CC/VlUyRj6RJtIwo=

View File

@@ -55,6 +55,12 @@ const (
// NotificationEventTypeGet captures enum value "get"
NotificationEventTypeGet NotificationEventType = "get"
// NotificationEventTypeReplica captures enum value "replica"
NotificationEventTypeReplica NotificationEventType = "replica"
// NotificationEventTypeIlm captures enum value "ilm"
NotificationEventTypeIlm NotificationEventType = "ilm"
)
// for schema
@@ -62,7 +68,7 @@ var notificationEventTypeEnum []interface{}
func init() {
var res []NotificationEventType
if err := json.Unmarshal([]byte(`["put","delete","get"]`), &res); err != nil {
if err := json.Unmarshal([]byte(`["put","delete","get","replica","ilm"]`), &res); err != nil {
panic(err)
}
for _, v := range res {

View File

@@ -291,6 +291,8 @@ export enum NotificationEventType {
Put = "put",
Delete = "delete",
Get = "get",
Replica = "replica",
Ilm = "ilm",
}
export interface NotificationConfig {

View File

@@ -14,53 +14,28 @@
// 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, { ChangeEvent, useCallback, useEffect, useState } from "react";
import Grid from "@mui/material/Grid";
import { Button, EventSubscriptionIcon } from "mds";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import TableCell from "@mui/material/TableCell";
import TableBody from "@mui/material/TableBody";
import Checkbox from "@mui/material/Checkbox";
import Table from "@mui/material/Table";
import {
formFieldStyles,
modalStyleUtils,
} from "../../Common/FormComponents/common/styleLibrary";
import React, { useCallback, useEffect, useState } from "react";
import { Button, DataTable, EventSubscriptionIcon, Grid, InputBox } from "mds";
import { ErrorResponseHandler } from "../../../../common/types";
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import AutocompleteWrapper from "../../Common/FormComponents/AutocompleteWrapper/AutocompleteWrapper";
import { setModalErrorSnackMessage } from "../../../../systemSlice";
import { useAppDispatch } from "../../../../store";
import { api } from "api";
import { NotificationEventType } from "api/consoleApi";
const styles = (theme: Theme) =>
createStyles({
arnField: {
"& div div .MuiOutlinedInput-root": {
padding: 0,
},
},
...formFieldStyles,
...modalStyleUtils,
});
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
import AutocompleteWrapper from "../../Common/FormComponents/AutocompleteWrapper/AutocompleteWrapper";
import {
formFieldStyles,
modalBasic,
modalStyleUtils,
} from "../../Common/FormComponents/common/styleLibrary";
interface IAddEventProps {
classes: any;
open: boolean;
selectedBucket: string;
closeModalAndRefresh: () => void;
}
const AddEvent = ({
classes,
open,
selectedBucket,
closeModalAndRefresh,
@@ -125,28 +100,27 @@ const AddEvent = ({
{ label: "PUT - Object Uploaded", value: NotificationEventType.Put },
{ label: "GET - Object accessed", value: NotificationEventType.Get },
{ label: "DELETE - Object Deleted", value: NotificationEventType.Delete },
{
label: "REPLICA - Object Replicated",
value: NotificationEventType.Replica,
},
{ label: "ILM - Object Transitioned", value: NotificationEventType.Ilm },
];
const handleClick = (
event: React.MouseEvent<unknown> | ChangeEvent<unknown>,
name: NotificationEventType
) => {
const selectedIndex = selectedEvents.indexOf(name);
let newSelected: NotificationEventType[] = [];
const handleClick = (event: React.ChangeEvent<HTMLInputElement>) => {
const targetD = event.target;
const value = targetD.value;
const checked = targetD.checked;
if (selectedIndex === -1) {
newSelected = newSelected.concat(selectedEvents, name);
} else if (selectedIndex === 0) {
newSelected = newSelected.concat(selectedEvents.slice(1));
} else if (selectedIndex === selectedEvents.length - 1) {
newSelected = newSelected.concat(selectedEvents.slice(0, -1));
} else if (selectedIndex > 0) {
newSelected = newSelected.concat(
selectedEvents.slice(0, selectedIndex),
selectedEvents.slice(selectedIndex + 1)
);
let elements: NotificationEventType[] = [...selectedEvents];
if (checked) {
elements.push(value as NotificationEventType);
} else {
elements = elements.filter((element) => element !== value);
}
setSelectedEvents(newSelected);
setSelectedEvents(elements);
};
const arnValues = arnList?.map((arnConstant) => ({
@@ -171,11 +145,16 @@ const AddEvent = ({
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
<Grid item xs={12} sx={modalBasic.formScrollable}>
<Grid
item
xs={12}
className={`${classes.arnField} ${classes.formFieldRow}`}
sx={{
...formFieldStyles.formFieldRow,
"& div div .MuiOutlinedInput-root": {
padding: 0,
},
}}
>
<AutocompleteWrapper
onChange={(value: string) => {
@@ -188,8 +167,8 @@ const AddEvent = ({
options={arnValues || []}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
<Grid item xs={12} sx={formFieldStyles.formFieldRow}>
<InputBox
id="prefix-input"
name="prefix-input"
label="Prefix"
@@ -199,8 +178,8 @@ const AddEvent = ({
}}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
<Grid item xs={12} sx={formFieldStyles.formFieldRow}>
<InputBox
id="suffix-input"
name="suffix-input"
label="Suffix"
@@ -210,41 +189,19 @@ const AddEvent = ({
}}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<Table size="medium">
<TableHead className={classes.minTableHeader}>
<TableRow>
<TableCell>Select</TableCell>
<TableCell>Event</TableCell>
</TableRow>
</TableHead>
<TableBody>
{events.map((row) => (
<TableRow
key={`group-${row.value}`}
onClick={(event) => handleClick(event, row.value)}
>
<TableCell padding="checkbox">
<Checkbox
value={row.value}
color="primary"
inputProps={{
"aria-label": "secondary checkbox",
}}
onChange={(event) => handleClick(event, row.value)}
checked={selectedEvents.includes(row.value)}
/>
</TableCell>
<TableCell className={classes.wrapCell}>
{row.label}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Grid item xs={12} sx={formFieldStyles.formFieldRow}>
<DataTable
columns={[{ label: "Event", elementKey: "label" }]}
idField={"value"}
records={events}
onSelect={handleClick}
selectedItems={selectedEvents}
noBackground
customPaperHeight={"260px"}
/>
</Grid>
</Grid>
<Grid item xs={12} className={classes.modalButtonBar}>
<Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
<Button
id={"cancel-add-event"}
type="button"
@@ -259,7 +216,7 @@ const AddEvent = ({
id={"save-event"}
type="submit"
variant="callAction"
disabled={addLoading}
disabled={addLoading || arn === "" || selectedEvents.length === 0}
label={"Save"}
/>
</Grid>
@@ -269,4 +226,4 @@ const AddEvent = ({
);
};
export default withStyles(styles)(AddEvent);
export default AddEvent;

View File

@@ -15,53 +15,30 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import get from "lodash/get";
import { useSelector } from "react-redux";
import { useParams } from "react-router-dom";
import { Theme } from "@mui/material/styles";
import { AddIcon, Button, HelpBox, LambdaIcon } from "mds";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import get from "lodash/get";
import Grid from "@mui/material/Grid";
import {
actionsTray,
searchField,
} from "../../Common/FormComponents/common/styleLibrary";
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
import PanelTitle from "../../Common/PanelTitle/PanelTitle";
import { AddIcon, Button, HelpBox, LambdaIcon, DataTable, Grid } from "mds";
import { api } from "api";
import { NotificationConfig } from "api/consoleApi";
import { errorToHandler } from "api/errors";
import { actionsTray } from "../../Common/FormComponents/common/styleLibrary";
import {
hasPermission,
SecureComponent,
} from "../../../../common/SecureComponent";
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
import withSuspense from "../../Common/Components/withSuspense";
import { setErrorSnackMessage, setHelpName } from "../../../../systemSlice";
import { selBucketDetailsLoading } from "./bucketDetailsSlice";
import { useAppDispatch } from "../../../../store";
import PanelTitle from "../../Common/PanelTitle/PanelTitle";
import withSuspense from "../../Common/Components/withSuspense";
import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
import { api } from "api";
import { NotificationConfig } from "api/consoleApi";
import { errorToHandler } from "api/errors";
const DeleteEvent = withSuspense(React.lazy(() => import("./DeleteEvent")));
const AddEvent = withSuspense(React.lazy(() => import("./AddEvent")));
const styles = (theme: Theme) =>
createStyles({
...searchField,
...actionsTray,
twHeight: {
minHeight: 400,
},
});
interface IBucketEventsProps {
classes: any;
}
const BucketEventsPanel = ({ classes }: IBucketEventsProps) => {
const BucketEventsPanel = () => {
const dispatch = useAppDispatch();
const params = useParams();
@@ -155,7 +132,7 @@ const BucketEventsPanel = ({ classes }: IBucketEventsProps) => {
)}
<Grid container>
<Grid item xs={12} className={classes.actionsTray}>
<Grid item xs={12} sx={actionsTray.actionsTray}>
<PanelTitle>Events</PanelTitle>
<SecureComponent
scopes={[
@@ -189,7 +166,7 @@ const BucketEventsPanel = ({ classes }: IBucketEventsProps) => {
resource={bucketName}
errorProps={{ disabled: true }}
>
<TableWrapper
<DataTable
itemActions={tableActions}
columns={[
{ label: "SQS", elementKey: "arn" },
@@ -205,7 +182,7 @@ const BucketEventsPanel = ({ classes }: IBucketEventsProps) => {
records={records}
entityName="Events"
idField="id"
customPaperHeight={classes.twHeight}
customPaperHeight={"400px"}
/>
</SecureComponent>
</Grid>
@@ -242,4 +219,4 @@ const BucketEventsPanel = ({ classes }: IBucketEventsProps) => {
);
};
export default withStyles(styles)(BucketEventsPanel);
export default BucketEventsPanel;

View File

@@ -14,11 +14,8 @@
// 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 from "react";
import React, { Fragment } from "react";
import get from "lodash/get";
import { DialogContentText } from "@mui/material";
import { ErrorResponseHandler } from "../../../../common/types";
import useApi from "../../Common/Hooks/useApi";
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
@@ -82,9 +79,7 @@ const DeleteEvent = ({
onConfirm={onConfirmDelete}
onClose={onClose}
confirmationContent={
<DialogContentText>
Are you sure you want to delete this event?
</DialogContentText>
<Fragment>Are you sure you want to delete this event?</Fragment>
}
/>
);

View File

@@ -7433,7 +7433,9 @@ func init() {
"enum": [
"put",
"delete",
"get"
"get",
"replica",
"ilm"
]
},
"objectBucketLifecycle": {
@@ -16564,7 +16566,9 @@ func init() {
"enum": [
"put",
"delete",
"get"
"get",
"replica",
"ilm"
]
},
"objectBucketLifecycle": {

View File

@@ -73,7 +73,14 @@ func listBucketEvents(client MinioClient, bucketName string) ([]*models.Notifica
eventTypePretty = models.NotificationEventTypePut
case notification.ObjectRemovedAll:
eventTypePretty = models.NotificationEventTypeDelete
case notification.ObjectReplicationAll:
eventTypePretty = models.NotificationEventTypeReplica
case notification.ObjectTransitionAll:
eventTypePretty = models.NotificationEventTypeIlm
default:
continue
}
result = append(result, eventTypePretty)
}
return result

View File

@@ -4044,6 +4044,8 @@ definitions:
- put
- delete
- get
- replica
- ilm
notificationConfig:
type: object
required: