Updated Tiers module to be its own page (#1113)

Also refactored & did some cleanup in the code

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>

Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
Co-authored-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
Alex
2021-10-15 14:35:04 -05:00
committed by GitHub
parent aadc66a739
commit 4d458c4f8c
19 changed files with 628 additions and 500 deletions

View File

@@ -26,6 +26,9 @@ var (
notificationEndpoints = "/notification-endpoints"
notificationEndpointsAddAny = "/notification-endpoints/add/:service"
notificationEndpointsAdd = "/notification-endpoints/add"
tiers = "/tiers"
tiersAddAny = "/tiers/add/:service"
tiersAdd = "/tiers/add"
users = "/users"
usersDetail = "/users/:userName+"
groups = "/groups"
@@ -294,6 +297,9 @@ var endpointRules = map[string]ConfigurationActionSet{
notificationEndpoints: configurationActionSet,
notificationEndpointsAdd: configurationActionSet,
notificationEndpointsAddAny: configurationActionSet,
tiers: configurationActionSet,
tiersAdd: configurationActionSet,
tiersAddAny: configurationActionSet,
users: usersActionSet,
usersDetail: usersActionSet,
groups: groupsActionSet,

View File

@@ -70,7 +70,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
"admin:*",
},
},
want: 26,
want: 29,
},
{
name: "all s3 endpoints",
@@ -89,7 +89,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
"s3:*",
},
},
want: 28,
want: 31,
},
{
name: "Console User - default endpoints",

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 304 182" style="enable-background:new 0 0 304 182;" xml:space="preserve">
<style type="text/css">
.st0{fill:#252F3E;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#FF9900;}
</style>
<g>
<path class="st0" d="M86.4,66.4c0,3.7,0.4,6.7,1.1,8.9c0.8,2.2,1.8,4.6,3.2,7.2c0.5,0.8,0.7,1.6,0.7,2.3c0,1-0.6,2-1.9,3l-6.3,4.2
c-0.9,0.6-1.8,0.9-2.6,0.9c-1,0-2-0.5-3-1.4C76.2,90,75,88.4,74,86.8c-1-1.7-2-3.6-3.1-5.9c-7.8,9.2-17.6,13.8-29.4,13.8
c-8.4,0-15.1-2.4-20-7.2c-4.9-4.8-7.4-11.2-7.4-19.2c0-8.5,3-15.4,9.1-20.6c6.1-5.2,14.2-7.8,24.5-7.8c3.4,0,6.9,0.3,10.6,0.8
c3.7,0.5,7.5,1.3,11.5,2.2v-7.3c0-7.6-1.6-12.9-4.7-16c-3.2-3.1-8.6-4.6-16.3-4.6c-3.5,0-7.1,0.4-10.8,1.3c-3.7,0.9-7.3,2-10.8,3.4
c-1.6,0.7-2.8,1.1-3.5,1.3c-0.7,0.2-1.2,0.3-1.6,0.3c-1.4,0-2.1-1-2.1-3.1v-4.9c0-1.6,0.2-2.8,0.7-3.5c0.5-0.7,1.4-1.4,2.8-2.1
c3.5-1.8,7.7-3.3,12.6-4.5c4.9-1.3,10.1-1.9,15.6-1.9c11.9,0,20.6,2.7,26.2,8.1c5.5,5.4,8.3,13.6,8.3,24.6V66.4z M45.8,81.6
c3.3,0,6.7-0.6,10.3-1.8c3.6-1.2,6.8-3.4,9.5-6.4c1.6-1.9,2.8-4,3.4-6.4c0.6-2.4,1-5.3,1-8.7v-4.2c-2.9-0.7-6-1.3-9.2-1.7
c-3.2-0.4-6.3-0.6-9.4-0.6c-6.7,0-11.6,1.3-14.9,4c-3.3,2.7-4.9,6.5-4.9,11.5c0,4.7,1.2,8.2,3.7,10.6
C37.7,80.4,41.2,81.6,45.8,81.6z M126.1,92.4c-1.8,0-3-0.3-3.8-1c-0.8-0.6-1.5-2-2.1-3.9L96.7,10.2c-0.6-2-0.9-3.3-0.9-4
c0-1.6,0.8-2.5,2.4-2.5h9.8c1.9,0,3.2,0.3,3.9,1c0.8,0.6,1.4,2,2,3.9l16.8,66.2l15.6-66.2c0.5-2,1.1-3.3,1.9-3.9c0.8-0.6,2.2-1,4-1
h8c1.9,0,3.2,0.3,4,1c0.8,0.6,1.5,2,1.9,3.9l15.8,67l17.3-67c0.6-2,1.3-3.3,2-3.9c0.8-0.6,2.1-1,3.9-1h9.3c1.6,0,2.5,0.8,2.5,2.5
c0,0.5-0.1,1-0.2,1.6c-0.1,0.6-0.3,1.4-0.7,2.5l-24.1,77.3c-0.6,2-1.3,3.3-2.1,3.9c-0.8,0.6-2.1,1-3.8,1h-8.6c-1.9,0-3.2-0.3-4-1
c-0.8-0.7-1.5-2-1.9-4L156,23l-15.4,64.4c-0.5,2-1.1,3.3-1.9,4c-0.8,0.7-2.2,1-4,1H126.1z M254.6,95.1c-5.2,0-10.4-0.6-15.4-1.8
c-5-1.2-8.9-2.5-11.5-4c-1.6-0.9-2.7-1.9-3.1-2.8c-0.4-0.9-0.6-1.9-0.6-2.8v-5.1c0-2.1,0.8-3.1,2.3-3.1c0.6,0,1.2,0.1,1.8,0.3
c0.6,0.2,1.5,0.6,2.5,1c3.4,1.5,7.1,2.7,11,3.5c4,0.8,7.9,1.2,11.9,1.2c6.3,0,11.2-1.1,14.6-3.3c3.4-2.2,5.2-5.4,5.2-9.5
c0-2.8-0.9-5.1-2.7-7c-1.8-1.9-5.2-3.6-10.1-5.2L246,52c-7.3-2.3-12.7-5.7-16-10.2c-3.3-4.4-5-9.3-5-14.5c0-4.2,0.9-7.9,2.7-11.1
c1.8-3.2,4.2-6,7.2-8.2c3-2.3,6.4-4,10.4-5.2c4-1.2,8.2-1.7,12.6-1.7c2.2,0,4.5,0.1,6.7,0.4c2.3,0.3,4.4,0.7,6.5,1.1
c2,0.5,3.9,1,5.7,1.6c1.8,0.6,3.2,1.2,4.2,1.8c1.4,0.8,2.4,1.6,3,2.5c0.6,0.8,0.9,1.9,0.9,3.3v4.7c0,2.1-0.8,3.2-2.3,3.2
c-0.8,0-2.1-0.4-3.8-1.2c-5.7-2.6-12.1-3.9-19.2-3.9c-5.7,0-10.2,0.9-13.3,2.8c-3.1,1.9-4.7,4.8-4.7,8.9c0,2.8,1,5.2,3,7.1
c2,1.9,5.7,3.8,11,5.5l14.2,4.5c7.2,2.3,12.4,5.5,15.5,9.6c3.1,4.1,4.6,8.8,4.6,14c0,4.3-0.9,8.2-2.6,11.6
c-1.8,3.4-4.2,6.4-7.3,8.8c-3.1,2.5-6.8,4.3-11.1,5.6C264.4,94.4,259.7,95.1,254.6,95.1z"/>
<g>
<path class="st1" d="M273.5,143.7c-32.9,24.3-80.7,37.2-121.8,37.2c-57.6,0-109.5-21.3-148.7-56.7c-3.1-2.8-0.3-6.6,3.4-4.4
c42.4,24.6,94.7,39.5,148.8,39.5c36.5,0,76.6-7.6,113.5-23.2C274.2,133.6,278.9,139.7,273.5,143.7z"/>
<path class="st1" d="M287.2,128.1c-4.2-5.4-27.8-2.6-38.5-1.3c-3.2,0.4-3.7-2.4-0.8-4.5c18.8-13.2,49.7-9.4,53.3-5
c3.6,4.5-1,35.4-18.6,50.2c-2.7,2.3-5.3,1.1-4.1-1.9C282.5,155.7,291.4,133.4,287.2,128.1z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="77.43" viewBox="0 0 19.68 15.24"><path d="M9.105 14.43l4.642-.82.043-.01-2.387-2.84a403.945 403.945 0 0 1-2.387-2.853c0-.014 2.465-6.802 2.479-6.826.004-.008 1.682 2.888 4.066 7.02l4.09 7.09.031.054-7.587-.001-7.587-.001 4.597-.812zM0 13.566c0-.004 1.125-1.957 2.5-4.34L5 4.893l2.913-2.445C9.515 1.104 10.83.002 10.836 0a.512.512 0 0 1-.047.118L7.625 6.903l-3.107 6.663-2.259.003c-1.242.002-2.259 0-2.259-.004z" fill="#0089d6"/></svg>

After

Width:  |  Height:  |  Size: 494 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"><path d="M40.728 20.488l2.05.035 5.57-5.57.27-2.36C44.2 8.657 38.367 6.26 31.993 6.26c-11.54 0-21.28 7.852-24.163 18.488.608-.424 1.908-.106 1.908-.106l11.13-1.83s.572-.947.862-.9A13.88 13.88 0 0 1 32 17.375c3.3.007 6.34 1.173 8.728 3.102z" fill="#ea4335"/><path d="M56.17 24.77c-1.293-4.77-3.958-8.982-7.555-12.177l-7.887 7.887c3.16 2.55 5.187 6.452 5.187 10.82v1.392c3.837 0 6.954 3.124 6.954 6.954 0 3.837-3.124 6.954-6.954 6.954H32.007L30.615 48v8.346l1.392 1.385h13.908A18.11 18.11 0 0 0 64 39.647c-.007-6.155-3.1-11.6-7.83-14.876z" fill="#4285f4"/><path d="M18.085 57.74h13.9V46.6h-13.9a6.89 6.89 0 0 1-2.862-.622l-2.007.615-5.57 5.57-.488 1.88a18 18 0 0 0 10.926 3.689z" fill="#34a853"/><path d="M18.085 21.57A18.11 18.11 0 0 0 0 39.654c0 5.873 2.813 11.095 7.166 14.403l8.064-8.064a6.96 6.96 0 0 1-4.099-6.339c0-3.837 3.124-6.954 6.954-6.954 2.82 0 5.244 1.7 6.34 4.1l8.064-8.064c-3.307-4.353-8.53-7.166-14.403-7.166z" fill="#fbbc05"/></svg>

After

Width:  |  Height:  |  Size: 1012 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 162.612 24.465" xmlns:v="https://vecta.io/nano"><path d="M52.751.414h9.108v23.63h-9.108zM41.711.74l-18.488 9.92a.919.919 0 0 1-.856 0L3.879.74A2.808 2.808 0 0 0 2.558.414h-.023A2.4 2.4 0 0 0 0 2.641v21.376h9.1V13.842a.918.918 0 0 1 1.385-.682l10.361 5.568a3.634 3.634 0 0 0 3.336.028l10.933-5.634a.917.917 0 0 1 1.371.69v10.205h9.1V2.641A2.4 2.4 0 0 0 43.055.414h-.023a2.808 2.808 0 0 0-1.321.326zm65.564-.326h-9.237v10.755a.913.913 0 0 1-1.338.706L72.762.675a2.824 2.824 0 0 0-1.191-.261h-.016a2.4 2.4 0 0 0-2.535 2.227v21.377h9.163V13.275a.914.914 0 0 1 1.337-.707l24.032 11.2a2.813 2.813 0 0 0 1.188.26 2.4 2.4 0 0 0 2.535-2.227zm7.161 23.63V.414h4.191v23.63zm28.856.421c-11.274 0-19.272-4.7-19.272-12.232C124.02 4.741 132.066 0 143.292 0s19.32 4.7 19.32 12.233-7.902 12.232-19.32 12.232zm0-21.333c-8.383 0-14.84 3.217-14.84 9.1 0 5.926 6.457 9.1 14.84 9.1s14.887-3.174 14.887-9.1c0-5.883-6.504-9.1-14.887-9.1z" fill="#c72c48"/></svg>

After

Width:  |  Height:  |  Size: 990 B

View File

@@ -376,6 +376,17 @@ export const settingsCommon = {
backContainer: {
margin: "20px 38px 0",
},
mainCont: {
paddingLeft: 50,
paddingRight: 50,
},
mainTitle: {
fontSize: 18,
color: "#000",
fontWeight: 600,
marginBottom: 10,
marginTop: 10,
},
};
export const typesSelection = {

View File

@@ -20,7 +20,6 @@ import { Grid } from "@material-ui/core";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
import ConfigurationsList from "./ConfigurationPanels/ConfigurationsList";
import ListTiersConfiguration from "./TiersConfiguration/ListTiersConfiguration";
import { AppState } from "../../../store";
import { connect } from "react-redux";
import { ISessionResponse } from "../types";
@@ -67,15 +66,6 @@ const ConfigurationMain = ({
>
<ListItemText primary="Configurations" />
</ListItem>
<ListItem
button
selected={selectedTab === 2}
onClick={() => {
setSelectedTab(2);
}}
>
<ListItemText primary="Tiers" />
</ListItem>
</List>
</Grid>
<Grid item xs={10}>
@@ -85,12 +75,6 @@ const ConfigurationMain = ({
<ConfigurationsList />
</Grid>
)}
{selectedTab === 2 && distributedSetup && (
<Grid item xs={12}>
<h1 className={classes.sectionTitle}>Tiers</h1>
<ListTiersConfiguration />
</Grid>
)}
</Grid>
</Grid>
</Fragment>

View File

@@ -16,18 +16,28 @@
import React, { Fragment, useEffect, useState, useCallback } from "react";
import { connect } from "react-redux";
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 api from "../../../../common/api";
import { setErrorSnackMessage } from "../../../../actions";
import {
modalBasic,
settingsCommon,
} from "../../Common/FormComponents/common/styleLibrary";
import { ErrorResponseHandler } from "../../../../common/types";
import api from "../../../../common/api";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FileSelector from "../../Common/FormComponents/FileSelector/FileSelector";
import BackSettingsIcon from "../../../../icons/BackSettingsIcon";
import PageHeader from "../../Common/PageHeader/PageHeader";
import {
azureServiceName,
gcsServiceName,
minioServiceName,
s3ServiceName,
tierTypes,
} from "./utils";
const styles = (theme: Theme) =>
createStyles({
@@ -50,20 +60,62 @@ const styles = (theme: Theme) =>
...settingsCommon.settingsFormContainer,
height: "calc(100vh - 422px)",
},
lambdaNotif: {
background:
"linear-gradient(90deg, rgba(249,249,250,1) 0%, rgba(250,250,251,1) 68%, rgba(254,254,254,1) 100%)",
border: "#E5E5E5 1px solid",
borderRadius: 5,
height: 80,
display: "flex",
alignItems: "center",
justifyContent: "start",
marginBottom: 16,
marginRight: 8,
cursor: "pointer",
padding: 0,
overflow: "hidden",
},
lambdaNotifIcon: {
backgroundColor: "#FEFEFE",
display: "flex",
alignItems: "center",
justifyContent: "center",
width: 80,
height: 80,
"& img": {
maxWidth: 46,
maxHeight: 46,
},
},
lambdaNotifTitle: {
color: "#07193E",
fontSize: 16,
fontFamily: "Lato,sans-serif",
paddingLeft: 18,
},
mainCont: {
maxWidth: 1180,
paddingLeft: 38,
paddingRight: 38,
},
backTo: {
margin: "20px 0px 0",
},
});
interface IAddNotificationEndpointProps {
saveAndRefresh: any;
setErrorSnackMessage: typeof setErrorSnackMessage;
classes: any;
type: string;
match: any;
history: any;
}
const AddTierConfiguration = ({
saveAndRefresh,
classes,
setErrorSnackMessage,
type,
match,
history,
}: IAddNotificationEndpointProps) => {
//Local States
const [saving, setSaving] = useState<boolean>(false);
@@ -87,6 +139,8 @@ const AddTierConfiguration = ({
const [titleSelection, setTitleSelection] = useState<string>("");
const type = get(match, "params.service", "s3");
// Validations
const [isFormValid, setIsFormValid] = useState<boolean>(true);
const [nameInputError, setNameInputError] = useState<string>("");
@@ -164,7 +218,8 @@ const AddTierConfiguration = ({
.invoke("POST", `/api/v1/admin/tiers`, payload)
.then(() => {
setSaving(false);
saveAndRefresh();
history.push("/tiers");
})
.catch((err: ErrorResponseHandler) => {
setSaving(false);
@@ -178,10 +233,10 @@ const AddTierConfiguration = ({
bucket,
encodedCreds,
endpoint,
history,
name,
prefix,
region,
saveAndRefresh,
saving,
secretKey,
setErrorSnackMessage,
@@ -283,158 +338,206 @@ const AddTierConfiguration = ({
setName(e.target.value.toUpperCase());
};
const backClick = () => {
history.push("/tiers/add");
};
const targetElement = tierTypes.find((item) => item.serviceName === type);
return (
<Fragment>
<form noValidate onSubmit={submitForm}>
<Grid item xs={12} className={classes.customTitle}>
{titleSelection} - Add Tier Configuration
<PageHeader label="Tiers" />
<Grid container className={classes.mainCont}>
<Grid item xs={12} className={classes.backTo}>
<button onClick={backClick} className={classes.backButton}>
<BackSettingsIcon />
Back To Tier Type Selection
</button>
</Grid>
<Grid item xs={12} className={classes.settingsFormContainer}>
<Grid container>
{type !== "" && (
<Fragment>
<InputBoxWrapper
id="name"
name="name"
label="Name"
placeholder="Enter Name (Eg. REMOTE-TIER)"
value={name}
onChange={updateTierName}
error={nameInputError}
/>
<InputBoxWrapper
id="endpoint"
name="endpoint"
label="Endpoint"
placeholder="Enter Endpoint"
value={endpoint}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setEndpoint(e.target.value);
}}
/>
{(type === "s3" || type === "minio") && (
{type !== "" && (
<Fragment>
<Grid item xs={12}>
{targetElement && (
<div
key={`icon-${targetElement.targetTitle}`}
className={classes.lambdaNotif}
>
<div className={classes.lambdaNotifIcon}>
<img
src={targetElement.logo}
className={classes.logoButton}
alt={targetElement.targetTitle}
/>
</div>
<div className={classes.lambdaNotifTitle}>
<b>
{titleSelection ? titleSelection : ""} Tier Configuration
</b>
</div>
</div>
)}
</Grid>
</Fragment>
)}
<Grid item xs={12}>
<form noValidate onSubmit={submitForm}>
<Grid item xs={12}>
<Grid container>
{type !== "" && (
<Fragment>
<InputBoxWrapper
id="accessKey"
name="accessKey"
label="Access Key"
placeholder="Enter Access Key"
value={accessKey}
id="name"
name="name"
label="Name"
placeholder="Enter Name (Eg. REMOTE-TIER)"
value={name}
onChange={updateTierName}
error={nameInputError}
/>
<InputBoxWrapper
id="endpoint"
name="endpoint"
label="Endpoint"
placeholder="Enter Endpoint"
value={endpoint}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccessKey(e.target.value);
setEndpoint(e.target.value);
}}
/>
{(type === s3ServiceName || type === minioServiceName) && (
<Fragment>
<InputBoxWrapper
id="accessKey"
name="accessKey"
label="Access Key"
placeholder="Enter Access Key"
value={accessKey}
onChange={(
e: React.ChangeEvent<HTMLInputElement>
) => {
setAccessKey(e.target.value);
}}
/>
<InputBoxWrapper
id="secretKey"
name="secretKey"
label="Secret Key"
placeholder="Enter Secret Key"
value={secretKey}
onChange={(
e: React.ChangeEvent<HTMLInputElement>
) => {
setSecretKey(e.target.value);
}}
/>
</Fragment>
)}
{type === gcsServiceName && (
<Fragment>
<FileSelector
accept=".json"
id="creds"
label="Credentials"
name="creds"
onChange={(encodedValue, fileName) => {
setEncodedCreds(encodedValue);
setCreds(fileName);
}}
value={creds}
/>
</Fragment>
)}
{type === azureServiceName && (
<Fragment>
<InputBoxWrapper
id="accountName"
name="accountName"
label="Account Name"
placeholder="Enter Account Name"
value={accountName}
onChange={(
e: React.ChangeEvent<HTMLInputElement>
) => {
setAccountName(e.target.value);
}}
/>
<InputBoxWrapper
id="accountKey"
name="accountKey"
label="Account Key"
placeholder="Enter Account Key"
value={accountKey}
onChange={(
e: React.ChangeEvent<HTMLInputElement>
) => {
setAccountKey(e.target.value);
}}
/>
</Fragment>
)}
<InputBoxWrapper
id="bucket"
name="bucket"
label="Bucket"
placeholder="Enter Bucket"
value={bucket}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setBucket(e.target.value);
}}
/>
<InputBoxWrapper
id="secretKey"
name="secretKey"
label="Secret Key"
placeholder="Enter Secret Key"
value={secretKey}
id="prefix"
name="prefix"
label="Prefix"
placeholder="Enter Prefix"
value={prefix}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSecretKey(e.target.value);
setPrefix(e.target.value);
}}
/>
<InputBoxWrapper
id="region"
name="region"
label="Region"
placeholder="Enter Region"
value={region}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setRegion(e.target.value);
}}
/>
{type === s3ServiceName ||
(type === minioServiceName && (
<InputBoxWrapper
id="storageClass"
name="storageClass"
label="Storage Class"
placeholder="Enter Storage Class"
value={storageClass}
onChange={(
e: React.ChangeEvent<HTMLInputElement>
) => {
setStorageClass(e.target.value);
}}
/>
))}
</Fragment>
)}
{type === "gcs" && (
<Fragment>
<FileSelector
accept=".json"
id="creds"
label="Credentials"
name="creds"
onChange={(encodedValue, fileName) => {
setEncodedCreds(encodedValue);
setCreds(fileName);
}}
value={creds}
/>
</Fragment>
)}
{type === "azure" && (
<Fragment>
<InputBoxWrapper
id="accountName"
name="accountName"
label="Account Name"
placeholder="Enter Account Name"
value={accountName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccountName(e.target.value);
}}
/>
<InputBoxWrapper
id="accountKey"
name="accountKey"
label="Account Key"
placeholder="Enter Account Key"
value={accountKey}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccountKey(e.target.value);
}}
/>
</Fragment>
)}
<InputBoxWrapper
id="bucket"
name="bucket"
label="Bucket"
placeholder="Enter Bucket"
value={bucket}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setBucket(e.target.value);
}}
/>
<InputBoxWrapper
id="prefix"
name="prefix"
label="Prefix"
placeholder="Enter Prefix"
value={prefix}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setPrefix(e.target.value);
}}
/>
<InputBoxWrapper
id="region"
name="region"
label="Region"
placeholder="Enter Region"
value={region}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setRegion(e.target.value);
}}
/>
{type === "s3" ||
(type === "minio" && (
<InputBoxWrapper
id="storageClass"
name="storageClass"
label="Storage Class"
placeholder="Enter Storage Class"
value={storageClass}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setStorageClass(e.target.value);
}}
/>
))}
</Fragment>
)}
</Grid>
</Grid>
</Grid>
<Grid item xs={12} className={classes.settingsButtonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={saving || !isFormValid}
>
Save
</Button>
</Grid>
</form>
</Grid>
<Grid item xs={12} className={classes.settingsButtonContainer}>
<Grid item xs={12} className={classes.innerSettingsButtonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={saving || !isFormValid}
>
Save
</Button>
</Grid>
</Grid>
</form>
</Grid>
</Fragment>
);
};

View File

@@ -35,16 +35,15 @@ import { ITierElement, ITierResponse } from "./types";
import { ErrorResponseHandler } from "../../../../common/types";
import api from "../../../../common/api";
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
import SlideOptions from "../../Common/SlideOptions/SlideOptions";
import BackSettingsIcon from "../../../../icons/BackSettingsIcon";
import AddTierConfiguration from "./AddTierConfiguration";
import UpdateTierCredentiasModal from "./UpdateTierCredentiasModal";
import RefreshIcon from "../../../../icons/RefreshIcon";
import SearchIcon from "../../../../icons/SearchIcon";
import history from "../../../../history";
import PageHeader from "../../Common/PageHeader/PageHeader";
interface IListTiersConfig {
classes: any;
history: any;
setErrorSnackMessage: typeof setErrorSnackMessage;
}
@@ -65,7 +64,7 @@ const styles = (theme: Theme) =>
lineHeight: "24px",
},
customConfigurationPage: {
height: "calc(100vh - 410px)",
height: "calc(100vh - 210px)",
scrollbarWidth: "none" as const,
"&::-webkit-scrollbar": {
display: "none",
@@ -76,7 +75,6 @@ const styles = (theme: Theme) =>
},
actionsTray: {
...actionsTray.actionsTray,
padding: "0 38px",
},
customTitle: {
...settingsCommon.customTitle,
@@ -86,18 +84,17 @@ const styles = (theme: Theme) =>
const ListTiersConfiguration = ({
classes,
history,
setErrorSnackMessage,
}: IListTiersConfig) => {
const [records, setRecords] = useState<ITierElement[]>([]);
const [filter, setFilter] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(true);
const [currentPanel, setCurrentPanel] = useState<number>(0);
const [updateCredentialsOpen, setUpdateCredentialsOpen] =
useState<boolean>(false);
const [selectedTier, setSelectedTier] = useState<ITierElement>({
type: "unsupported",
});
const [type, setType] = useState<string>("");
useEffect(() => {
if (isLoading) {
@@ -127,17 +124,8 @@ const ListTiersConfiguration = ({
return getItemName.indexOf(filter) >= 0 || getItemType.indexOf(filter) >= 0;
});
const backClick = () => {
setCurrentPanel(currentPanel - 1);
};
const addTier = () => {
setCurrentPanel(1);
};
const tierAdded = () => {
setCurrentPanel(0);
setIsLoading(true);
history.push("/tiers/add");
};
const renderTierName = (item: ITierElement) => {
@@ -194,11 +182,6 @@ const ListTiersConfiguration = ({
setUpdateCredentialsOpen(false);
};
const typeSelect = (typeItem: string) => {
setType(typeItem);
setCurrentPanel(2);
};
return (
<Fragment>
{updateCredentialsOpen && (
@@ -208,238 +191,107 @@ const ListTiersConfiguration = ({
closeModalAndRefresh={closeTierCredentials}
/>
)}
<Grid container>
<Grid item xs={12}>
<Grid item xs={12}>
<div className={classes.settingsOptionsContainer}>
<SlideOptions
slideOptions={[
<Fragment>
<Grid item xs={12} className={classes.lambdaContainer}>
<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>
),
}}
/>
<IconButton
color="primary"
aria-label="Refresh List"
component="span"
onClick={() => {
setIsLoading(true);
}}
>
<RefreshIcon />
</IconButton>
<Button
variant="contained"
color="primary"
startIcon={<AddIcon />}
onClick={addTier}
>
Add Tier
</Button>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<TableWrapper
itemActions={[
{
type: "edit",
onClick: (tierData: ITierElement) => {
setSelectedTier(tierData);
setUpdateCredentialsOpen(true);
},
},
]}
columns={[
{
label: "Tier Name",
elementKey: "type",
renderFunction: renderTierName,
renderFullObject: true,
},
{
label: "Type",
elementKey: "type",
width: 150,
},
{
label: "Endpoint",
elementKey: "type",
renderFunction: renderTierEndpoint,
renderFullObject: true,
},
{
label: "Bucket",
elementKey: "type",
renderFunction: renderTierBucket,
renderFullObject: true,
},
{
label: "Prefix",
elementKey: "type",
renderFunction: renderTierPrefix,
renderFullObject: true,
},
{
label: "Region",
elementKey: "type",
renderFunction: renderTierRegion,
renderFullObject: true,
},
]}
isLoading={isLoading}
records={filteredRecords}
entityName="Tiers"
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 Tiers
</button>
</Grid>
<Grid item xs={12}>
<Grid item xs={12} className={classes.customTitle}>
Add Tier Configuration
</Grid>
<Grid
item
xs={12}
className={classes.settingsFormContainer}
>
<Grid item xs={12}>
<Grid item xs={12} className={classes.centerElements}>
<div className={classes.iconContainer}>
<button
className={classes.lambdaNotif}
onClick={() => {
typeSelect("minio");
}}
>
<div className={classes.lambdaNotifIcon}>
<img
src={"/minioTier.png"}
className={classes.logoButton}
alt={"MinIO"}
/>
</div>
<div className={classes.lambdaNotifTitle}>
<b>MinIO</b>
</div>
</button>
<button
className={classes.lambdaNotif}
onClick={() => {
typeSelect("gcs");
}}
>
<div className={classes.lambdaNotifIcon}>
<img
src={"/gcs.png"}
className={classes.logoButton}
alt={"GCS"}
/>
</div>
<div className={classes.lambdaNotifTitle}>
<b>Google Cloud Storage</b>
</div>
</button>
<button
className={classes.lambdaNotif}
onClick={() => {
typeSelect("s3");
}}
>
<div className={classes.lambdaNotifIcon}>
<img
src={"/amazon.png"}
className={classes.logoButton}
alt={"s3"}
/>
</div>
<div className={classes.lambdaNotifTitle}>
<b>AWS S3</b>
</div>
</button>
<button
className={classes.lambdaNotif}
onClick={() => {
typeSelect("azure");
}}
>
<div className={classes.lambdaNotifIcon}>
<img
src={"/azure.png"}
className={classes.logoButton}
alt={"Azure"}
/>
</div>
<div className={classes.lambdaNotifTitle}>
<b>Azure Blob Storage</b>
</div>
</button>
</div>
</Grid>
</Grid>
</Grid>
</Grid>
</Fragment>,
<Fragment>
<Grid item xs={12} className={classes.backContainer}>
<button
onClick={backClick}
className={classes.backButton}
>
<BackSettingsIcon />
Back To Tier Type Selection
</button>
</Grid>
<Grid item xs={12}>
{currentPanel === 2 && (
<AddTierConfiguration
type={type}
saveAndRefresh={tierAdded}
/>
)}
</Grid>
</Fragment>,
]}
currentSlide={currentPanel}
<PageHeader label="Tiers" />
<Grid container className={classes.container}>
<Fragment>
<Grid item xs={12} className={classes.lambdaContainer}>
<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>
),
}}
/>
</div>
<IconButton
color="primary"
aria-label="Refresh List"
component="span"
onClick={() => {
setIsLoading(true);
}}
>
<RefreshIcon />
</IconButton>
<Button
variant="contained"
color="primary"
startIcon={<AddIcon />}
onClick={addTier}
>
Add Tier
</Button>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<TableWrapper
itemActions={[
{
type: "edit",
onClick: (tierData: ITierElement) => {
setSelectedTier(tierData);
setUpdateCredentialsOpen(true);
},
},
]}
columns={[
{
label: "Tier Name",
elementKey: "type",
renderFunction: renderTierName,
renderFullObject: true,
},
{
label: "Type",
elementKey: "type",
width: 150,
},
{
label: "Endpoint",
elementKey: "type",
renderFunction: renderTierEndpoint,
renderFullObject: true,
},
{
label: "Bucket",
elementKey: "type",
renderFunction: renderTierBucket,
renderFullObject: true,
},
{
label: "Prefix",
elementKey: "type",
renderFunction: renderTierPrefix,
renderFullObject: true,
},
{
label: "Region",
elementKey: "type",
renderFunction: renderTierRegion,
renderFullObject: true,
},
]}
isLoading={isLoading}
records={filteredRecords}
entityName="Tiers"
idField="service_name"
customPaperHeight={classes.customConfigurationPage}
/>
</Grid>
</Grid>
</Grid>
</Fragment>
</Grid>
</Fragment>
);

View File

@@ -0,0 +1,120 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 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 { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import {
actionsTray,
containerForHeader,
searchField,
settingsCommon,
typesSelection,
} from "../../Common/FormComponents/common/styleLibrary";
import PageHeader from "../../Common/PageHeader/PageHeader";
import { tierTypes } from "./utils";
interface ITypeTiersConfig {
classes: any;
history: any;
}
const styles = (theme: Theme) =>
createStyles({
...actionsTray,
...searchField,
...settingsCommon,
...typesSelection,
...containerForHeader(theme.spacing(4)),
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",
},
customTitle: {
...settingsCommon.customTitle,
marginTop: 0,
},
});
const TierTypeSelector = ({ classes, history }: ITypeTiersConfig) => {
const typeSelect = (selectName: string) => {
history.push(`/tiers/add/${selectName}`);
};
return (
<Fragment>
<PageHeader label="Tier Configuration" />
<Grid container className={classes.mainCont}>
<Grid item xs={12}>
<Grid item xs={12} className={classes.mainTitle}>
Pick a supported Configuration
</Grid>
<Grid item xs={12} className={classes.settingsFormContainer}>
<Grid item xs={12}>
<Grid item xs={12} className={classes.centerElements}>
<div className={classes.iconContainer}>
{tierTypes.map((tierType, index) => (
<button
className={classes.lambdaNotif}
onClick={() => {
typeSelect(tierType.serviceName);
}}
key={`tierOpt-${index.toString}-${tierType.targetTitle}`}
>
<div className={classes.lambdaNotifIcon}>
<img
src={tierType.logo}
className={classes.logoButton}
alt={tierType.targetTitle}
/>
</div>
<div className={classes.lambdaNotifTitle}>
<b>{tierType.targetTitle}</b>
</div>
</button>
))}
</div>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</Fragment>
);
};
export default withStyles(styles)(TierTypeSelector);

View File

@@ -0,0 +1,43 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 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/>.
export const minioServiceName = "minio";
export const gcsServiceName = "gcs";
export const s3ServiceName = "s3";
export const azureServiceName = "azure";
export const tierTypes = [
{
serviceName: minioServiceName,
targetTitle: "MinIO",
logo: "/minio-logo.svg",
},
{
serviceName: gcsServiceName,
targetTitle: "Google Cloud Storage",
logo: "/gcs-logo.svg",
},
{
serviceName: s3ServiceName,
targetTitle: "AWS S3",
logo: "/aws-logo.svg",
},
{
serviceName: azureServiceName,
targetTitle: "Azure",
logo: "/azure-logo.svg",
},
];

View File

@@ -59,6 +59,9 @@ import AddTenant from "./Tenants/AddTenant/AddTenant";
import NotificationEndpoints from "./NotificationEndpoints/NotificationEndpoints";
import AddNotificationEndpoint from "./NotificationEndpoints/AddNotificationEndpoint";
import NotificationTypeSelector from "./NotificationEndpoints/NotificationTypeSelector";
import ListTiersConfiguration from "./Configurations/TiersConfiguration/ListTiersConfiguration";
import TierTypeSelector from "./Configurations/TiersConfiguration/TierTypeSelector";
import AddTierConfiguration from "./Configurations/TiersConfiguration/AddTierConfiguration";
const drawerWidth = 245;
@@ -293,6 +296,18 @@ const Console = ({
component: NotificationEndpoints,
path: "/notification-endpoints",
},
{
component: AddTierConfiguration,
path: "/tiers/add/:service",
},
{
component: TierTypeSelector,
path: "/tiers/add",
},
{
component: ListTiersConfiguration,
path: "/tiers",
},
{
component: Account,
path: "/account",

View File

@@ -27,10 +27,7 @@ import { widgetDetailsToPanel } from "../utils";
import { CircularProgress } from "@material-ui/core";
import { ErrorResponseHandler } from "../../../../../common/types";
import api from "../../../../../common/api";
import {
prettyNumber,
representationNumber,
} from "../../../../../common/utils";
import { representationNumber } from "../../../../../common/utils";
interface ISingleRepWidget {
classes: any;

View File

@@ -39,6 +39,7 @@ import {
GroupsIcon,
IAMPoliciesIcon,
LambdaIcon,
TiersIcon,
TraceIcon,
UsersIcon,
VersionIcon,
@@ -375,6 +376,14 @@ const Menu = ({
name: "Notification Endpoints",
icon: <LambdaIcon />,
},
{
group: "common",
type: "item",
component: NavLink,
to: "/tiers",
name: "Tiers",
icon: <TiersIcon />,
},
{
group: "Tools",
type: "item",

View File

@@ -225,8 +225,8 @@ const AddNotificationEndpoint = ({
<div className={classes.lambdaNotifTitle}>
<b>
{targetElement ? targetElement.targetTitle : ""} Lambda
Notification Target
{targetElement ? targetElement.targetTitle : ""}
Notification Endpoint
</b>
</div>
</div>

View File

@@ -31,7 +31,6 @@ import {
import { notificationTransform } from "./utils";
import { AddIcon } from "../../../icons";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import AddNotificationEndpoint from "./AddNotificationEndpoint";
import { setErrorSnackMessage } from "../../../actions";
import {
actionsTray,
@@ -41,9 +40,6 @@ import {
} from "../Common/FormComponents/common/styleLibrary";
import { ErrorResponseHandler } from "../../../common/types";
import api from "../../../common/api";
import SlideOptions from "../Common/SlideOptions/SlideOptions";
import BackSettingsIcon from "../../../icons/BackSettingsIcon";
import NotificationTypeSelector from "./NotificationTypeSelector";
import RefreshIcon from "../../../icons/RefreshIcon";
import SearchIcon from "../../../icons/SearchIcon";
import history from "../../../history";
@@ -88,8 +84,6 @@ const ListNotificationEndpoints = ({
const [records, setRecords] = useState<TransformedEndpointItem[]>([]);
const [filter, setFilter] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(false);
const [currentPanel, setCurrentPanel] = useState<number>(0);
const [service, setService] = useState<string>("");
//Effects
// load records on mount
@@ -142,21 +136,6 @@ const ListNotificationEndpoints = ({
);
};
const openNewLambdaSelector = () => {
setCurrentPanel(1);
};
const backClick = () => {
setService("");
setCurrentPanel(currentPanel - 1);
};
const saveAndRefresh = () => {
setIsLoading(true);
setCurrentPanel(0);
setService("");
};
return (
<Fragment>
<Grid container>
@@ -202,49 +181,30 @@ const ListNotificationEndpoints = ({
<Grid item xs={12}>
<Grid item xs={12}>
<div className={classes.settingsOptionsContainer}>
<SlideOptions
slideOptions={[
<Fragment>
<Grid item xs={12} className={classes.lambdaContainer}>
<Grid item xs={12}>
<TableWrapper
itemActions={[]}
columns={[
{
label: "Status",
elementKey: "status",
renderFunction: statusDisplay,
width: 150,
},
{ label: "Service", elementKey: "service_name" },
]}
isLoading={isLoading}
records={filteredRecords}
entityName="Lambda Notification Targets"
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 />
</Grid>
</Fragment>,
]}
currentSlide={currentPanel}
/>
<Fragment>
<Grid item xs={12} className={classes.lambdaContainer}>
<Grid item xs={12}>
<TableWrapper
itemActions={[]}
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>
</div>
</Grid>
</Grid>

View File

@@ -14,7 +14,7 @@
// 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, useState } from "react";
import React, { Fragment } from "react";
import PageHeader from "../Common/PageHeader/PageHeader";
import { Grid } from "@material-ui/core";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
@@ -47,8 +47,6 @@ const NotificationEndpoints = ({
session,
distributedSetup,
}: INotificationEndpoints) => {
const [selectedTab, setSelectedTab] = useState<number>(0);
return (
<Fragment>
<PageHeader label="Notification Endpoints" />

View File

@@ -14,7 +14,7 @@
// 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, useState } from "react";
import React, { Fragment } from "react";
import Grid from "@material-ui/core/Grid";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { servicesList } from "./utils";
@@ -34,17 +34,6 @@ const withLogos = servicesList.filter((elService) => elService.logo !== "");
const styles = (theme: Theme) =>
createStyles({
...settingsCommon,
mainCont: {
paddingLeft: 50,
paddingRight: 50,
},
mainTitle: {
fontSize: 18,
color: "#000",
fontWeight: 600,
marginBottom: 10,
marginTop: 10,
},
...typesSelection,
});