github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/BucketDetails/BucketEventsPanel.tsx (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 import React, { Fragment, useEffect, useState } from "react"; 18 import get from "lodash/get"; 19 import { useSelector } from "react-redux"; 20 import { useParams } from "react-router-dom"; 21 import { 22 AddIcon, 23 Button, 24 HelpBox, 25 LambdaIcon, 26 DataTable, 27 Grid, 28 SectionTitle, 29 HelpTip, 30 } from "mds"; 31 import { api } from "api"; 32 import { NotificationConfig } from "api/consoleApi"; 33 import { errorToHandler } from "api/errors"; 34 import { 35 hasPermission, 36 SecureComponent, 37 } from "../../../../common/SecureComponent"; 38 import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions"; 39 import { setErrorSnackMessage, setHelpName } from "../../../../systemSlice"; 40 import { selBucketDetailsLoading } from "./bucketDetailsSlice"; 41 import { useAppDispatch } from "../../../../store"; 42 import withSuspense from "../../Common/Components/withSuspense"; 43 import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper"; 44 45 const DeleteEvent = withSuspense(React.lazy(() => import("./DeleteEvent"))); 46 const AddEvent = withSuspense(React.lazy(() => import("./AddEvent"))); 47 48 const BucketEventsPanel = () => { 49 const dispatch = useAppDispatch(); 50 const params = useParams(); 51 52 const loadingBucket = useSelector(selBucketDetailsLoading); 53 54 const [addEventScreenOpen, setAddEventScreenOpen] = useState<boolean>(false); 55 const [loadingEvents, setLoadingEvents] = useState<boolean>(true); 56 const [records, setRecords] = useState<NotificationConfig[]>([]); 57 const [deleteOpen, setDeleteOpen] = useState<boolean>(false); 58 const [selectedEvent, setSelectedEvent] = useState<NotificationConfig | null>( 59 null, 60 ); 61 62 const bucketName = params.bucketName || ""; 63 64 const displayEvents = hasPermission(bucketName, [ 65 IAM_SCOPES.S3_GET_BUCKET_NOTIFICATIONS, 66 IAM_SCOPES.S3_GET_ACTIONS, 67 ]); 68 69 useEffect(() => { 70 if (loadingBucket) { 71 setLoadingEvents(true); 72 } 73 }, [loadingBucket, setLoadingEvents]); 74 75 useEffect(() => { 76 dispatch(setHelpName("bucket_detail_events")); 77 // eslint-disable-next-line react-hooks/exhaustive-deps 78 }, []); 79 80 useEffect(() => { 81 if (loadingEvents) { 82 if (displayEvents) { 83 api.buckets 84 .listBucketEvents(bucketName) 85 .then((res) => { 86 const events = get(res.data, "events", []); 87 setLoadingEvents(false); 88 setRecords(events || []); 89 }) 90 .catch((err) => { 91 setLoadingEvents(false); 92 dispatch(setErrorSnackMessage(errorToHandler(err.error))); 93 }); 94 } else { 95 setLoadingEvents(false); 96 } 97 } 98 }, [loadingEvents, dispatch, bucketName, displayEvents]); 99 100 const eventsDisplay = (events: string[] | null) => { 101 if (!events) { 102 return "other"; 103 } 104 105 const cleanEvents = events.reduce((acc: string[], read: string) => { 106 if (!acc.includes(read)) { 107 return [...acc, read]; 108 } 109 return acc; 110 }, []); 111 112 return <Fragment>{cleanEvents.join(", ")}</Fragment>; 113 }; 114 115 const confirmDeleteEvent = (evnt: NotificationConfig) => { 116 setDeleteOpen(true); 117 setSelectedEvent(evnt); 118 }; 119 120 const closeAddEventAndRefresh = () => { 121 setAddEventScreenOpen(false); 122 setLoadingEvents(true); 123 }; 124 125 const closeDeleteModalAndRefresh = (refresh: boolean) => { 126 setDeleteOpen(false); 127 if (refresh) { 128 setLoadingEvents(true); 129 } 130 }; 131 132 const tableActions = [{ type: "delete", onClick: confirmDeleteEvent }]; 133 134 return ( 135 <Fragment> 136 {deleteOpen && ( 137 <DeleteEvent 138 deleteOpen={deleteOpen} 139 selectedBucket={bucketName} 140 bucketEvent={selectedEvent} 141 closeDeleteModalAndRefresh={closeDeleteModalAndRefresh} 142 /> 143 )} 144 {addEventScreenOpen && ( 145 <AddEvent 146 open={addEventScreenOpen} 147 selectedBucket={bucketName} 148 closeModalAndRefresh={closeAddEventAndRefresh} 149 /> 150 )} 151 152 <SectionTitle 153 separator 154 sx={{ marginBottom: 15 }} 155 actions={ 156 <SecureComponent 157 scopes={[ 158 IAM_SCOPES.S3_PUT_BUCKET_NOTIFICATIONS, 159 IAM_SCOPES.S3_PUT_ACTIONS, 160 IAM_SCOPES.ADMIN_SERVER_INFO, 161 ]} 162 resource={bucketName} 163 matchAll 164 errorProps={{ disabled: true }} 165 > 166 <TooltipWrapper tooltip={"Subscribe to Event"}> 167 <Button 168 id={"Subscribe-bucket-event"} 169 onClick={() => { 170 setAddEventScreenOpen(true); 171 }} 172 label={"Subscribe to Event"} 173 icon={<AddIcon />} 174 variant={"callAction"} 175 /> 176 </TooltipWrapper> 177 </SecureComponent> 178 } 179 > 180 <HelpTip 181 content={ 182 <Fragment> 183 MinIO{" "} 184 <a 185 target="blank" 186 href="https://min.io/docs/minio/kubernetes/upstream/administration/monitoring.html" 187 > 188 bucket notifications 189 </a>{" "} 190 allow administrators to send notifications to supported external 191 services on certain object or bucket events. 192 </Fragment> 193 } 194 placement="right" 195 > 196 Events 197 </HelpTip> 198 </SectionTitle> 199 200 <Grid container> 201 <Grid item xs={12}> 202 <SecureComponent 203 scopes={[ 204 IAM_SCOPES.S3_GET_BUCKET_NOTIFICATIONS, 205 IAM_SCOPES.S3_GET_ACTIONS, 206 ]} 207 resource={bucketName} 208 errorProps={{ disabled: true }} 209 > 210 <DataTable 211 itemActions={tableActions} 212 columns={[ 213 { label: "SQS", elementKey: "arn" }, 214 { 215 label: "Events", 216 elementKey: "events", 217 renderFunction: eventsDisplay, 218 }, 219 { label: "Prefix", elementKey: "prefix" }, 220 { label: "Suffix", elementKey: "suffix" }, 221 ]} 222 isLoading={loadingEvents} 223 records={records} 224 entityName="Events" 225 idField="id" 226 customPaperHeight={"400px"} 227 /> 228 </SecureComponent> 229 </Grid> 230 {!loadingEvents && ( 231 <Grid item xs={12}> 232 <br /> 233 <HelpBox 234 title={"Event Notifications"} 235 iconComponent={<LambdaIcon />} 236 help={ 237 <Fragment> 238 MinIO bucket notifications allow administrators to send 239 notifications to supported external services on certain object 240 or bucket events. MinIO supports bucket and object-level S3 241 events similar to the Amazon S3 Event Notifications. 242 <br /> 243 <br /> 244 You can learn more at our{" "} 245 <a 246 href="https://min.io/docs/minio/linux/administration/monitoring/bucket-notifications.html?ref=con" 247 target="_blank" 248 rel="noopener" 249 > 250 documentation 251 </a> 252 . 253 </Fragment> 254 } 255 /> 256 </Grid> 257 )} 258 </Grid> 259 </Fragment> 260 ); 261 }; 262 263 export default BucketEventsPanel;