Split Object Browser and Buckets Admin (#2500)
Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
1285
.github/workflows/jobs.yaml
vendored
1285
.github/workflows/jobs.yaml
vendored
File diff suppressed because it is too large
Load Diff
@@ -20,7 +20,6 @@
|
||||
"@types/react-dom": "18.0.9",
|
||||
"@types/react-grid-layout": "^1.1.1",
|
||||
"@types/react-redux": "^7.1.24",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-virtualized": "^9.21.21",
|
||||
"@types/superagent": "^4.1.16",
|
||||
"@types/webpack-env": "^1.14.1",
|
||||
@@ -79,7 +78,7 @@
|
||||
"@types/recharts": "^1.8.24",
|
||||
"prettier": "2.7.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"testcafe": "^1.18.6",
|
||||
"testcafe": "^2.1.0",
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
@@ -146,7 +146,7 @@ test("Can Browse Bucket", () => {
|
||||
expect(
|
||||
hasPermission(
|
||||
"bucket-svc",
|
||||
IAM_PAGES_PERMISSIONS[IAM_PAGES.BUCKETS_BROWSE_VIEW]
|
||||
IAM_PAGES_PERMISSIONS[IAM_PAGES.OBJECT_BROWSER_VIEW]
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
@@ -166,7 +166,7 @@ test("Can browse a bucket for a policy with a wildcard", () => {
|
||||
expect(
|
||||
hasPermission(
|
||||
"testbucket-0",
|
||||
IAM_PAGES_PERMISSIONS[IAM_PAGES.BUCKETS_BROWSE_VIEW]
|
||||
IAM_PAGES_PERMISSIONS[IAM_PAGES.OBJECT_BROWSER_VIEW]
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
@@ -117,7 +117,10 @@ export const IAM_PAGES = {
|
||||
BUCKETS: "/buckets",
|
||||
ADD_BUCKETS: "add-bucket",
|
||||
BUCKETS_ADMIN_VIEW: ":bucketName/admin/*",
|
||||
BUCKETS_BROWSE_VIEW: ":bucketName/browse/*",
|
||||
/* Object Browser */
|
||||
OBJECT_BROWSER_VIEW: "/browser",
|
||||
OBJECT_BROWSER_BUCKET_VIEW: "/browser/:bucketName",
|
||||
OBJECT_BROWSER_BUCKET_DETAILS_VIEW: "/browser/:bucketName/*",
|
||||
/* Identity */
|
||||
IDENTITY: "/identity",
|
||||
USERS: "/identity/users",
|
||||
@@ -307,7 +310,7 @@ export const IAM_PAGES_PERMISSIONS = {
|
||||
[IAM_PAGES.BUCKETS_ADMIN_VIEW]: [
|
||||
...IAM_PERMISSIONS[IAM_ROLES.BUCKET_ADMIN], // bucket admin page
|
||||
],
|
||||
[IAM_PAGES.BUCKETS_BROWSE_VIEW]: [
|
||||
[IAM_PAGES.OBJECT_BROWSER_VIEW]: [
|
||||
...IAM_PERMISSIONS[IAM_ROLES.BUCKET_OWNER],
|
||||
...IAM_PERMISSIONS[IAM_ROLES.BUCKET_VIEWER],
|
||||
],
|
||||
|
||||
@@ -39,6 +39,7 @@ import BackLink from "../../../../common/BackLink";
|
||||
import {
|
||||
newMessage,
|
||||
resetMessages,
|
||||
setIsOpeningOD,
|
||||
setIsVersioned,
|
||||
setLoadingLocking,
|
||||
setLoadingObjectInfo,
|
||||
@@ -184,24 +185,23 @@ const BrowserHandler = () => {
|
||||
const loadingLocking = useSelector(
|
||||
(state: AppState) => state.objectBrowser.loadingLocking
|
||||
);
|
||||
const bucketToRewind = useSelector(
|
||||
(state: AppState) => state.objectBrowser.rewind.bucketToRewind
|
||||
);
|
||||
const loadRecords = useSelector(
|
||||
(state: AppState) => state.objectBrowser.loadRecords
|
||||
);
|
||||
const detailsOpen = useSelector(
|
||||
(state: AppState) => state.objectBrowser.objectDetailsOpen
|
||||
);
|
||||
const selectedInternalPaths = useSelector(
|
||||
(state: AppState) => state.objectBrowser.selectedInternalPaths
|
||||
);
|
||||
const simplePath = useSelector(
|
||||
(state: AppState) => state.objectBrowser.simplePath
|
||||
);
|
||||
const isOpeningOD = useSelector(
|
||||
(state: AppState) => state.objectBrowser.isOpeningObjectDetail
|
||||
);
|
||||
|
||||
const features = useSelector(selFeatures);
|
||||
|
||||
const bucketName = params.bucketName || "";
|
||||
const pathSegment = location.pathname.split("/browse/");
|
||||
|
||||
const pathSegment = location.pathname.split(`/browser/${bucketName}/`);
|
||||
const internalPaths = pathSegment.length === 2 ? pathSegment[1] : "";
|
||||
|
||||
const obOnly = !!features?.includes("object-browser-only");
|
||||
@@ -226,9 +226,12 @@ const BrowserHandler = () => {
|
||||
// Session expired.
|
||||
window.location.reload();
|
||||
} else if (response.error === "Access Denied.") {
|
||||
const internalPathsPrefix = response.prefix;
|
||||
let pathPrefix = "";
|
||||
if (internalPaths) {
|
||||
const decodedPath = decodeURLString(internalPaths);
|
||||
|
||||
if (internalPathsPrefix) {
|
||||
const decodedPath = decodeURLString(internalPathsPrefix);
|
||||
|
||||
pathPrefix = decodedPath.endsWith("/")
|
||||
? decodedPath
|
||||
: decodedPath + "/";
|
||||
@@ -266,7 +269,7 @@ const BrowserHandler = () => {
|
||||
}
|
||||
}
|
||||
},
|
||||
[dispatch, internalPaths, allowResources, bucketName]
|
||||
[dispatch, allowResources, bucketName]
|
||||
);
|
||||
|
||||
const initWSRequest = useCallback(
|
||||
@@ -317,54 +320,63 @@ const BrowserHandler = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (objectsWS?.readyState === 1) {
|
||||
const decodedIPaths = decodeURLString(internalPaths);
|
||||
const decodedIPaths = decodeURLString(internalPaths);
|
||||
|
||||
if (decodedIPaths.endsWith("/") || decodedIPaths === "") {
|
||||
dispatch(setObjectDetailsView(false));
|
||||
dispatch(setSelectedObjectView(null));
|
||||
dispatch(
|
||||
setSimplePathHandler(decodedIPaths === "" ? "/" : decodedIPaths)
|
||||
);
|
||||
} else {
|
||||
dispatch(setLoadingObjectInfo(true));
|
||||
dispatch(setObjectDetailsView(true));
|
||||
dispatch(setLoadingVersions(true));
|
||||
dispatch(
|
||||
setSelectedObjectView(
|
||||
`${decodedIPaths ? `${encodeURLString(decodedIPaths)}` : ``}`
|
||||
)
|
||||
);
|
||||
dispatch(
|
||||
setSimplePathHandler(
|
||||
`${decodedIPaths.split("/").slice(0, -1).join("/")}/`
|
||||
)
|
||||
);
|
||||
}
|
||||
dispatch(setLoadingVersioning(true));
|
||||
|
||||
if (decodedIPaths.endsWith("/") || decodedIPaths === "") {
|
||||
dispatch(setObjectDetailsView(false));
|
||||
dispatch(setSelectedObjectView(null));
|
||||
dispatch(
|
||||
setSimplePathHandler(decodedIPaths === "" ? "/" : decodedIPaths)
|
||||
);
|
||||
} else {
|
||||
dispatch(setLoadingObjectInfo(true));
|
||||
dispatch(setObjectDetailsView(true));
|
||||
dispatch(setLoadingVersions(true));
|
||||
dispatch(
|
||||
setSelectedObjectView(
|
||||
`${decodedIPaths ? `${encodeURLString(decodedIPaths)}` : ``}`
|
||||
)
|
||||
);
|
||||
dispatch(
|
||||
setSimplePathHandler(
|
||||
`${decodedIPaths.split("/").slice(0, -1).join("/")}/`
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [internalPaths, rewindDate, rewindEnabled, dispatch]);
|
||||
|
||||
// Direct file access effect / prefix
|
||||
useEffect(() => {
|
||||
if (!loadingObjects && loadRecords && !rewindEnabled) {
|
||||
const parentPath = `${decodeURLString(internalPaths)
|
||||
.split("/")
|
||||
.slice(0, -1)
|
||||
.join("/")}/`;
|
||||
if (!loadingObjects && !loadRecords && !rewindEnabled && !isOpeningOD) {
|
||||
// No requests are in progress, We review current path, if it doesn't end in '/' and current list is empty then we trigger a new request.
|
||||
const decodedInternalPaths = decodeURLString(internalPaths);
|
||||
|
||||
initWSRequest(parentPath, new Date());
|
||||
if (
|
||||
!decodedInternalPaths.endsWith("/") &&
|
||||
simplePath !== decodedInternalPaths &&
|
||||
decodedInternalPaths !== ""
|
||||
) {
|
||||
setLoadingRecords(true);
|
||||
const parentPath = `${decodedInternalPaths
|
||||
.split("/")
|
||||
.slice(0, -1)
|
||||
.join("/")}/`;
|
||||
|
||||
initWSRequest(parentPath, new Date());
|
||||
}
|
||||
}
|
||||
dispatch(setIsOpeningOD(false));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
loadingObjects,
|
||||
loadRecords,
|
||||
bucketName,
|
||||
bucketToRewind,
|
||||
dispatch,
|
||||
internalPaths,
|
||||
rewindDate,
|
||||
rewindEnabled,
|
||||
initWSRequest,
|
||||
detailsOpen,
|
||||
rewindEnabled,
|
||||
simplePath,
|
||||
]);
|
||||
|
||||
const displayListObjects = hasPermission(bucketName, [
|
||||
@@ -547,7 +559,12 @@ const BrowserHandler = () => {
|
||||
<Fragment>
|
||||
{!obOnly ? (
|
||||
<PageHeader
|
||||
label={<BackLink label={"Buckets"} to={IAM_PAGES.BUCKETS} />}
|
||||
label={
|
||||
<BackLink
|
||||
label={"Object Browser"}
|
||||
to={IAM_PAGES.OBJECT_BROWSER_VIEW}
|
||||
/>
|
||||
}
|
||||
actions={
|
||||
<SecureComponent
|
||||
scopes={IAM_PERMISSIONS[IAM_ROLES.BUCKET_ADMIN]}
|
||||
|
||||
@@ -200,10 +200,6 @@ const BucketDetails = ({ classes }: IBucketDetailsProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const openBucketBrowser = () => {
|
||||
navigate(`/buckets/${bucketName}/browse`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{deleteOpen && (
|
||||
@@ -231,7 +227,9 @@ const BucketDetails = ({ classes }: IBucketDetailsProps) => {
|
||||
<Button
|
||||
id={"switch-browse-view"}
|
||||
aria-label="Browse Bucket"
|
||||
onClick={openBucketBrowser}
|
||||
onClick={() => {
|
||||
navigate(`/browser/${bucketName}`);
|
||||
}}
|
||||
icon={
|
||||
<FolderIcon style={{ width: 20, height: 20, marginTop: -3 }} />
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
|
||||
|
||||
const ListBuckets = React.lazy(() => import("./ListBuckets/ListBuckets"));
|
||||
const BucketDetails = React.lazy(() => import("./BucketDetails/BucketDetails"));
|
||||
const BrowserHandler = React.lazy(
|
||||
() => import("./BucketDetails/BrowserHandler")
|
||||
);
|
||||
const AddBucket = React.lazy(() => import("./ListBuckets/AddBucket/AddBucket"));
|
||||
|
||||
const Buckets = () => {
|
||||
@@ -56,22 +53,6 @@ const Buckets = () => {
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path=":bucketName/browse/*"
|
||||
element={
|
||||
<Suspense fallback={<LoadingComponent />}>
|
||||
<BrowserHandler />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path=":bucketName/browse"
|
||||
element={
|
||||
<Suspense fallback={<LoadingComponent />}>
|
||||
<BrowserHandler />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route element={<Navigate to={`/buckets`} />} path="*" />
|
||||
|
||||
<Route
|
||||
|
||||
@@ -16,14 +16,10 @@
|
||||
import React, { Fragment } from "react";
|
||||
import get from "lodash/get";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import { Button } from "mds";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import {
|
||||
ArrowRightIcon,
|
||||
BucketsIcon,
|
||||
ReportedUsageIcon,
|
||||
SettingsIcon,
|
||||
TotalObjectsIcon,
|
||||
} from "../../../../icons";
|
||||
import { Bucket } from "../types";
|
||||
@@ -38,14 +34,12 @@ import { Link, useNavigate } from "react-router-dom";
|
||||
import {
|
||||
IAM_PERMISSIONS,
|
||||
IAM_ROLES,
|
||||
permissionTooltipHelper,
|
||||
} from "../../../../common/SecureComponent/permissions";
|
||||
import { SecureComponent } from "../../../../common/SecureComponent";
|
||||
import clsx from "clsx";
|
||||
import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
|
||||
import { hasPermission } from "../../../../common/SecureComponent";
|
||||
import clsx from "clsx";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
marginBottom: 30,
|
||||
@@ -53,6 +47,10 @@ const styles = (theme: Theme) =>
|
||||
color: theme.palette.primary.main,
|
||||
border: "#E5E5E5 1px solid",
|
||||
borderRadius: 2,
|
||||
textDecoration: "none",
|
||||
"&:hover": {
|
||||
backgroundColor: "#fafafa",
|
||||
},
|
||||
"& .min-icon": {
|
||||
height: 14,
|
||||
width: 14,
|
||||
@@ -161,11 +159,14 @@ const styles = (theme: Theme) =>
|
||||
marginTop: "-33px",
|
||||
},
|
||||
},
|
||||
});
|
||||
disabled: {
|
||||
backgroundColor: "red",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
interface IBucketListItem {
|
||||
bucket: Bucket;
|
||||
classes: any;
|
||||
onSelect: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
selected: boolean;
|
||||
bulkSelect: boolean;
|
||||
@@ -173,13 +174,13 @@ interface IBucketListItem {
|
||||
}
|
||||
|
||||
const BucketListItem = ({
|
||||
classes,
|
||||
bucket,
|
||||
onSelect,
|
||||
selected,
|
||||
bulkSelect,
|
||||
noManage = false,
|
||||
}: IBucketListItem) => {
|
||||
const classes = useStyles();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const usage = niceBytes(`${bucket.size}` || "0");
|
||||
@@ -189,10 +190,9 @@ const BucketListItem = ({
|
||||
const quota = get(bucket, "details.quota.quota", "0");
|
||||
const quotaForString = calculateBytes(quota, true, false);
|
||||
|
||||
const manageAllowed = hasPermission(
|
||||
bucket.name,
|
||||
IAM_PERMISSIONS[IAM_ROLES.BUCKET_ADMIN]
|
||||
);
|
||||
const manageAllowed =
|
||||
hasPermission(bucket.name, IAM_PERMISSIONS[IAM_ROLES.BUCKET_ADMIN]) &&
|
||||
false;
|
||||
|
||||
const accessToStr = (bucket: Bucket): string => {
|
||||
if (bucket.rw_access?.read && !bucket.rw_access?.write) {
|
||||
@@ -209,7 +209,19 @@ const BucketListItem = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid container className={clsx(classes.root, "bucket-item")}>
|
||||
<Grid
|
||||
container
|
||||
className={clsx(classes.root, "bucket-item", {
|
||||
[classes.disabled]: manageAllowed,
|
||||
})}
|
||||
onClick={() => {
|
||||
navigate(`/buckets/${bucket.name}/admin`);
|
||||
}}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
id={`manageBucket-${bucket.name}`}
|
||||
>
|
||||
<Grid item xs={12}>
|
||||
<Grid container justifyContent={"space-between"}>
|
||||
<Grid item xs={12} sm={7}>
|
||||
@@ -232,7 +244,9 @@ const BucketListItem = ({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<h1 className={classes.bucketName}>{bucket.name}</h1>
|
||||
<h1 className={classes.bucketName}>
|
||||
{bucket.name} {manageAllowed}
|
||||
</h1>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container className={classes.bucketInfo}>
|
||||
@@ -251,43 +265,6 @@ const BucketListItem = ({
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={5} className={classes.bucketActionButtons}>
|
||||
{!noManage && (
|
||||
<SecureComponent
|
||||
scopes={IAM_PERMISSIONS[IAM_ROLES.BUCKET_ADMIN]}
|
||||
resource={bucket.name}
|
||||
>
|
||||
<TooltipWrapper
|
||||
tooltip={
|
||||
manageAllowed
|
||||
? "Manage Bucket"
|
||||
: permissionTooltipHelper(
|
||||
IAM_PERMISSIONS[IAM_ROLES.BUCKET_ADMIN],
|
||||
"managing this bucket"
|
||||
)
|
||||
}
|
||||
>
|
||||
<Button
|
||||
onClick={() => navigate(`/buckets/${bucket.name}/admin`)}
|
||||
label={"Manage"}
|
||||
icon={<SettingsIcon />}
|
||||
color={"primary"}
|
||||
variant={"regular"}
|
||||
id={`manage-${bucket.name}`}
|
||||
disabled={!manageAllowed}
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
</SecureComponent>
|
||||
)}
|
||||
<TooltipWrapper tooltip={"Browse"}>
|
||||
<Button
|
||||
onClick={() => navigate(`/buckets/${bucket.name}/browse`)}
|
||||
label={"Browse"}
|
||||
icon={<ArrowRightIcon />}
|
||||
color={"primary"}
|
||||
variant={"callAction"}
|
||||
id={`browse-${bucket.name}`}
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
<Box display={{ xs: "none", sm: "block" }}>
|
||||
<div style={{ marginBottom: 10 }} />
|
||||
</Box>
|
||||
@@ -330,4 +307,4 @@ const BucketListItem = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(BucketListItem);
|
||||
export default BucketListItem;
|
||||
|
||||
@@ -106,7 +106,7 @@ const CreatePathModal = ({
|
||||
.filter((splitItem) => splitItem.trim() !== "")
|
||||
.join("/");
|
||||
|
||||
const newPath = `/buckets/${bucketName}/browse/${encodeURLString(
|
||||
const newPath = `/browser/${bucketName}/${encodeURLString(
|
||||
`${folderPath}${cleanPathURL}/`
|
||||
)}`;
|
||||
navigate(newPath);
|
||||
|
||||
@@ -283,11 +283,11 @@ const ListObjects = () => {
|
||||
const [canPreviewFile, setCanPreviewFile] = useState<boolean>(false);
|
||||
const [quota, setQuota] = useState<BucketQuota | null>(null);
|
||||
|
||||
const pathSegment = location.pathname.split("/browse/");
|
||||
|
||||
const internalPaths = pathSegment.length === 2 ? pathSegment[1] : "";
|
||||
const bucketName = params.bucketName || "";
|
||||
|
||||
const pathSegment = location.pathname.split(`/browser/${bucketName}/`);
|
||||
const internalPaths = pathSegment.length === 2 ? pathSegment[1] : "";
|
||||
|
||||
const pageTitle = decodeURLString(internalPaths);
|
||||
const currentPath = pageTitle.split("/").filter((i: string) => i !== "");
|
||||
|
||||
@@ -729,7 +729,7 @@ const ListObjects = () => {
|
||||
URLItem = `${splitURLS.join("/")}/`;
|
||||
}
|
||||
|
||||
navigate(`/buckets/${bucketName}/browse/${encodeURLString(URLItem)}`);
|
||||
navigate(`/browser/${bucketName}/${encodeURLString(URLItem)}`);
|
||||
}
|
||||
|
||||
dispatch(setObjectDetailsView(false));
|
||||
|
||||
@@ -27,6 +27,7 @@ import { AppState, useAppDispatch } from "../../../../../../store";
|
||||
import { selFeatures } from "../../../../consoleSlice";
|
||||
import { encodeURLString } from "../../../../../../common/utils";
|
||||
import {
|
||||
setIsOpeningOD,
|
||||
setLoadingObjects,
|
||||
setLoadingVersions,
|
||||
setObjectDetailsView,
|
||||
@@ -142,7 +143,7 @@ const ListObjectsTable = () => {
|
||||
const openPath = (idElement: string) => {
|
||||
dispatch(setSelectedObjects([]));
|
||||
|
||||
const newPath = `/buckets/${bucketName}/browse${
|
||||
const newPath = `/browser/${bucketName}${
|
||||
idElement ? `/${encodeURLString(idElement)}` : ``
|
||||
}`;
|
||||
navigate(newPath);
|
||||
@@ -154,6 +155,7 @@ const ListObjectsTable = () => {
|
||||
`${idElement ? `${encodeURLString(idElement)}` : ``}`
|
||||
)
|
||||
);
|
||||
dispatch(setIsOpeningOD(true));
|
||||
};
|
||||
const tableActions: ItemActions[] = [
|
||||
{
|
||||
|
||||
@@ -45,6 +45,7 @@ export interface WebsocketResponse {
|
||||
error?: string;
|
||||
request_end?: boolean;
|
||||
data?: ObjectResponse[];
|
||||
prefix?: string;
|
||||
}
|
||||
export interface ObjectResponse {
|
||||
name: string;
|
||||
|
||||
@@ -229,7 +229,7 @@ export const permissionItems = (
|
||||
// splitURL has more items than bucket name, we can continue validating
|
||||
if (splitURLARN.length > 1) {
|
||||
splitURLARN.every((currentElementInPath, index) => {
|
||||
// It is a wildcard element. We can stor the verification as value should be included (?)
|
||||
// It is a wildcard element. We can store the verification as value should be included (?)
|
||||
if (currentElementInPath === "*") {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -108,6 +108,8 @@ const ObjectManager = React.lazy(
|
||||
() => import("./Common/ObjectManager/ObjectManager")
|
||||
);
|
||||
|
||||
const ObjectBrowser = React.lazy(() => import("./ObjectBrowser/ObjectBrowser"));
|
||||
|
||||
const Buckets = React.lazy(() => import("./Buckets/Buckets"));
|
||||
const Policies = React.lazy(() => import("./Policies/Policies"));
|
||||
|
||||
@@ -272,6 +274,23 @@ const Console = ({ classes }: IConsoleProps) => {
|
||||
});
|
||||
|
||||
const consoleAdminRoutes: IRouteRule[] = [
|
||||
{
|
||||
component: ObjectBrowser,
|
||||
path: IAM_PAGES.OBJECT_BROWSER_VIEW,
|
||||
forceDisplay: true,
|
||||
customPermissionFnc: () => {
|
||||
const path = window.location.pathname;
|
||||
const resource = path.match(/browser\/(.*)\//);
|
||||
return (
|
||||
resource &&
|
||||
resource.length > 0 &&
|
||||
hasPermission(
|
||||
resource[1],
|
||||
IAM_PAGES_PERMISSIONS[IAM_PAGES.OBJECT_BROWSER_VIEW]
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
component: Buckets,
|
||||
path: IAM_PAGES.BUCKETS,
|
||||
@@ -304,22 +323,7 @@ const Console = ({ classes }: IConsoleProps) => {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
component: Buckets,
|
||||
path: IAM_PAGES.BUCKETS_BROWSE_VIEW,
|
||||
customPermissionFnc: () => {
|
||||
const path = window.location.pathname;
|
||||
const resource = path.match(/buckets\/(.*)\/browse*/);
|
||||
return (
|
||||
resource &&
|
||||
resource.length > 0 &&
|
||||
hasPermission(
|
||||
resource[1],
|
||||
IAM_PAGES_PERMISSIONS[IAM_PAGES.BUCKETS_BROWSE_VIEW]
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
component: Watch,
|
||||
path: IAM_PAGES.TOOLS_WATCH,
|
||||
|
||||
@@ -95,7 +95,7 @@ const BrowserBreadcrumbs = ({
|
||||
|
||||
let breadcrumbsMap = splitPaths.map((objectItem: string, index: number) => {
|
||||
const subSplit = `${splitPaths.slice(0, index + 1).join("/")}/`;
|
||||
const route = `/buckets/${bucketName}/browse/${
|
||||
const route = `/browser/${bucketName}/${
|
||||
subSplit ? `${encodeURLString(subSplit)}` : ``
|
||||
}`;
|
||||
|
||||
@@ -140,7 +140,7 @@ const BrowserBreadcrumbs = ({
|
||||
const listBreadcrumbs: any[] = [
|
||||
<Fragment key={`breadcrumbs-root-path`}>
|
||||
<Link
|
||||
to={`/buckets/${bucketName}/browse`}
|
||||
to={`/browser/${bucketName}`}
|
||||
onClick={() => {
|
||||
dispatch(setVersionsModeEnabled({ status: false, objectName: "" }));
|
||||
}}
|
||||
|
||||
312
portal-ui/src/screens/Console/ObjectBrowser/OBBucketList.tsx
Normal file
312
portal-ui/src/screens/Console/ObjectBrowser/OBBucketList.tsx
Normal file
@@ -0,0 +1,312 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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, useEffect, useState } from "react";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import { Button } from "mds";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import { LinearProgress } from "@mui/material";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { Bucket, BucketList } from "../Buckets/types";
|
||||
import { BucketsIcon } from "../../../icons";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import api from "../../../common/api";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
|
||||
import HelpBox from "../../../common/HelpBox";
|
||||
import RefreshIcon from "../../../icons/RefreshIcon";
|
||||
import { SecureComponent } from "../../../common/SecureComponent";
|
||||
import {
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_PAGES,
|
||||
IAM_SCOPES,
|
||||
permissionTooltipHelper,
|
||||
} from "../../../common/SecureComponent/permissions";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
import SearchBox from "../Common/SearchBox";
|
||||
import hasPermission from "../../../common/SecureComponent/accessControl";
|
||||
import { setErrorSnackMessage } from "../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../store";
|
||||
import { useSelector } from "react-redux";
|
||||
import { selFeatures } from "../consoleSlice";
|
||||
import AutoColorIcon from "../Common/Components/AutoColorIcon";
|
||||
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
|
||||
import AButton from "../Common/AButton/AButton";
|
||||
import { setLoadingObjects } from "../ObjectBrowser/objectBrowserSlice";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import { niceBytesInt } from "../../../common/utils";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
bulkSelect: {
|
||||
marginLeft: 8,
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
},
|
||||
"&.MuiButton-contained": {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
},
|
||||
},
|
||||
bucketList: {
|
||||
marginTop: 25,
|
||||
height: "calc(100vh - 211px)",
|
||||
"&.isEmbedded": {
|
||||
height: "calc(100vh - 128px)",
|
||||
},
|
||||
},
|
||||
searchField: {
|
||||
...searchField.searchField,
|
||||
minWidth: 380,
|
||||
"@media (max-width: 900px)": {
|
||||
minWidth: 220,
|
||||
},
|
||||
},
|
||||
...actionsTray,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
})
|
||||
);
|
||||
|
||||
const OBListBuckets = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
const classes = useStyles();
|
||||
|
||||
const [records, setRecords] = useState<Bucket[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [filterBuckets, setFilterBuckets] = useState<string>("");
|
||||
|
||||
const features = useSelector(selFeatures);
|
||||
const obOnly = !!features?.includes("object-browser-only");
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
const fetchRecords = () => {
|
||||
setLoading(true);
|
||||
api
|
||||
.invoke("GET", `/api/v1/buckets`)
|
||||
.then((res: BucketList) => {
|
||||
setLoading(false);
|
||||
setRecords(res.buckets || []);
|
||||
dispatch(setLoadingObjects(true));
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoading(false);
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
});
|
||||
};
|
||||
fetchRecords();
|
||||
}
|
||||
}, [loading, dispatch]);
|
||||
|
||||
const filteredRecords = records.filter((b: Bucket) => {
|
||||
if (filterBuckets === "") {
|
||||
return true;
|
||||
} else {
|
||||
return b.name.indexOf(filterBuckets) >= 0;
|
||||
}
|
||||
});
|
||||
|
||||
const hasBuckets = records.length > 0;
|
||||
|
||||
const canListBuckets = hasPermission("*", [IAM_SCOPES.S3_LIST_BUCKET]);
|
||||
|
||||
const tableActions = [
|
||||
{
|
||||
type: "view",
|
||||
onClick: (bucket: Bucket) => {
|
||||
navigate(`${IAM_PAGES.OBJECT_BROWSER_VIEW}/${bucket.name}`);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{!obOnly && <PageHeader label={"Object Browser"} />}
|
||||
<PageLayout>
|
||||
<Grid item xs={12} className={classes.actionsTray} display="flex">
|
||||
{obOnly && (
|
||||
<Grid item xs>
|
||||
<AutoColorIcon marginRight={15} marginTop={10} />
|
||||
</Grid>
|
||||
)}
|
||||
{hasBuckets && (
|
||||
<SearchBox
|
||||
onChange={setFilterBuckets}
|
||||
placeholder="Filter Buckets"
|
||||
overrideClass={classes.searchField}
|
||||
value={filterBuckets}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
justifyContent={"flex-end"}
|
||||
sx={{
|
||||
"& button": {
|
||||
marginLeft: "8px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TooltipWrapper tooltip={"Refresh"}>
|
||||
<Button
|
||||
id={"refresh-buckets"}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
}}
|
||||
icon={<RefreshIcon />}
|
||||
variant={"regular"}
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{loading && <LinearProgress />}
|
||||
{!loading && (
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={`${classes.bucketList} ${obOnly ? "isEmbedded" : ""}`}
|
||||
>
|
||||
{filteredRecords.length !== 0 && (
|
||||
<TableWrapper
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName={"Buckets"}
|
||||
idField={"name"}
|
||||
columns={[
|
||||
{
|
||||
label: "Name",
|
||||
elementKey: "name",
|
||||
renderFunction: (label) => (
|
||||
<span id={`browse-${label}`}>{label}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Objects",
|
||||
elementKey: "objects",
|
||||
renderFunction: (size: number) => size || 0,
|
||||
},
|
||||
{
|
||||
label: "Size",
|
||||
elementKey: "size",
|
||||
renderFunction: (size: number) => niceBytesInt(size || 0),
|
||||
},
|
||||
{
|
||||
label: "Access",
|
||||
renderFullObject: true,
|
||||
renderFunction: (bucket: Bucket) => {
|
||||
let access = [];
|
||||
if (bucket.rw_access?.read) {
|
||||
access.push("R");
|
||||
}
|
||||
if (bucket.rw_access?.write) {
|
||||
access.push("W");
|
||||
}
|
||||
return <span>{access.join("/")}</span>;
|
||||
},
|
||||
},
|
||||
]}
|
||||
itemActions={tableActions}
|
||||
/>
|
||||
)}
|
||||
{filteredRecords.length === 0 && filterBuckets !== "" && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
iconComponent={<BucketsIcon />}
|
||||
title={"No Results"}
|
||||
help={
|
||||
<Fragment>
|
||||
No buckets match the filtering condition
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
{!hasBuckets && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
iconComponent={<BucketsIcon />}
|
||||
title={"Buckets"}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO uses buckets to organize objects. A bucket is
|
||||
similar to a folder or directory in a filesystem, where
|
||||
each bucket can hold an arbitrary number of objects.
|
||||
<br />
|
||||
{canListBuckets ? (
|
||||
""
|
||||
) : (
|
||||
<Fragment>
|
||||
<br />
|
||||
{permissionTooltipHelper(
|
||||
[IAM_SCOPES.S3_LIST_BUCKET],
|
||||
"view the buckets on this server"
|
||||
)}
|
||||
<br />
|
||||
</Fragment>
|
||||
)}
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_CREATE_BUCKET]}
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
>
|
||||
<br />
|
||||
To get started,
|
||||
<AButton
|
||||
onClick={() => {
|
||||
navigate(IAM_PAGES.ADD_BUCKETS);
|
||||
}}
|
||||
>
|
||||
Create a Bucket.
|
||||
</AButton>
|
||||
</SecureComponent>
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
)}
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default OBListBuckets;
|
||||
@@ -0,0 +1,79 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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, { Suspense } from "react";
|
||||
import { Navigate, Route, Routes } from "react-router-dom";
|
||||
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
|
||||
import LoadingComponent from "../../../common/LoadingComponent";
|
||||
import NotFoundPage from "../../NotFoundPage";
|
||||
import OBBucketList from "./OBBucketList";
|
||||
|
||||
const BrowserHandler = React.lazy(
|
||||
() => import("../Buckets/BucketDetails/BrowserHandler")
|
||||
);
|
||||
const AddBucket = React.lazy(
|
||||
() => import("../Buckets/ListBuckets/AddBucket/AddBucket")
|
||||
);
|
||||
|
||||
const ObjectBrowser = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route
|
||||
path={IAM_PAGES.ADD_BUCKETS}
|
||||
element={
|
||||
<Suspense fallback={<LoadingComponent />}>
|
||||
<AddBucket />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<Suspense fallback={<LoadingComponent />}>
|
||||
<OBBucketList />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/:bucketName/*"
|
||||
element={
|
||||
<Suspense fallback={<LoadingComponent />}>
|
||||
<BrowserHandler />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path=":bucketName/"
|
||||
element={
|
||||
<Suspense fallback={<LoadingComponent />}>
|
||||
<BrowserHandler />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
<Route element={<Navigate to={`/browser`} />} path="*" />
|
||||
|
||||
<Route
|
||||
element={
|
||||
<Suspense fallback={<LoadingComponent />}>
|
||||
<NotFoundPage />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
export default ObjectBrowser;
|
||||
@@ -63,6 +63,7 @@ const initialState: ObjectBrowserState = {
|
||||
selectedPreview: null,
|
||||
previewOpen: false,
|
||||
shareFileModalOpen: false,
|
||||
isOpeningObjectDetail: false,
|
||||
};
|
||||
|
||||
export const objectBrowserSlice = createSlice({
|
||||
@@ -336,6 +337,9 @@ export const objectBrowserSlice = createSlice({
|
||||
);
|
||||
}
|
||||
},
|
||||
setIsOpeningOD: (state, action: PayloadAction<boolean>) => {
|
||||
state.isOpeningObjectDetail = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
export const {
|
||||
@@ -378,6 +382,7 @@ export const {
|
||||
setShareFileModalOpen,
|
||||
setLoadingRecords,
|
||||
restoreLocalObjectList,
|
||||
setIsOpeningOD,
|
||||
} = objectBrowserSlice.actions;
|
||||
|
||||
export default objectBrowserSlice.reducer;
|
||||
|
||||
@@ -89,6 +89,7 @@ export interface ObjectBrowserState {
|
||||
selectedPreview: BucketObjectItem | null;
|
||||
previewOpen: boolean;
|
||||
shareFileModalOpen: boolean;
|
||||
isOpeningObjectDetail: boolean;
|
||||
}
|
||||
|
||||
export interface ObjectManager {
|
||||
|
||||
@@ -70,7 +70,7 @@ export const routesAsKbarActions = (
|
||||
name: buck.name,
|
||||
section: "List of Buckets",
|
||||
perform: () => {
|
||||
navigate(`/buckets/${buck.name}/browse`);
|
||||
navigate(`/browser/${buck.name}`);
|
||||
},
|
||||
icon: <BucketsIcon />,
|
||||
}),
|
||||
|
||||
@@ -51,6 +51,7 @@ import {
|
||||
DocumentationIcon,
|
||||
LambdaIcon,
|
||||
LicenseIcon,
|
||||
ObjectBrowserIcon,
|
||||
RecoverIcon,
|
||||
StorageIcon,
|
||||
TenantsOutlineIcon,
|
||||
@@ -70,11 +71,11 @@ export const validRoutes = (
|
||||
let consoleMenus: IMenuItem[] = [
|
||||
{
|
||||
group: "User",
|
||||
name: "Buckets",
|
||||
id: "buckets",
|
||||
name: "Object Browser",
|
||||
id: "object-browser",
|
||||
component: NavLink,
|
||||
to: IAM_PAGES.BUCKETS,
|
||||
icon: BucketsMenuIcon,
|
||||
to: IAM_PAGES.OBJECT_BROWSER_VIEW,
|
||||
icon: ObjectBrowserIcon,
|
||||
forceDisplay: true,
|
||||
children: [],
|
||||
},
|
||||
@@ -108,7 +109,16 @@ export const validRoutes = (
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
group: "Administrator",
|
||||
name: "Buckets",
|
||||
id: "buckets",
|
||||
component: NavLink,
|
||||
to: IAM_PAGES.BUCKETS,
|
||||
icon: BucketsMenuIcon,
|
||||
forceDisplay: true,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
group: "Administrator",
|
||||
name: "Identity",
|
||||
|
||||
@@ -29,23 +29,6 @@ test("Buckets sidebar item exists", async (t) => {
|
||||
await t.useRole(roles.bucketWrite).expect(bucketsExist).ok();
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpBucket(t, "bucketwrite1");
|
||||
})("Browse button exists", async (t) => {
|
||||
const testBucketBrowseButton = testBucketBrowseButtonFor("bucketwrite1");
|
||||
await t
|
||||
.useRole(roles.bucketWrite)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.expect(testBucketBrowseButton.exists)
|
||||
.ok();
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket and corresponding uploads
|
||||
await functions.cleanUpBucketAndUploads(t, "bucketwrite1");
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
@@ -78,7 +61,7 @@ test
|
||||
const testBucketBrowseButton = testBucketBrowseButtonFor("bucketwrite2");
|
||||
await t
|
||||
.useRole(roles.bucketWrite)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButton)
|
||||
.expect(uploadExists)
|
||||
.ok();
|
||||
@@ -96,7 +79,7 @@ test
|
||||
const testBucketBrowseButton = testBucketBrowseButtonFor("bucketwrite3");
|
||||
await t
|
||||
.useRole(roles.bucketWrite)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButton)
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt");
|
||||
@@ -113,7 +96,7 @@ test
|
||||
})("Object list table is disabled", async (t) => {
|
||||
await t
|
||||
.useRole(roles.bucketWrite)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("bucketwrite4"))
|
||||
.expect(elements.bucketsTableDisabled.exists)
|
||||
.ok();
|
||||
|
||||
@@ -43,7 +43,7 @@ test
|
||||
})("Delete button is disabled for object inside bucket", async (t) => {
|
||||
await t
|
||||
.useRole(roles.deleteObjectWithPrefixOnly)
|
||||
.navigateTo(`http://localhost:9090/buckets`)
|
||||
.navigateTo(`http://localhost:9090/browser`)
|
||||
.click(test1BucketBrowseButton)
|
||||
.click(
|
||||
Selector(".ReactVirtualized__Table__rowColumn").withText("test.txt")
|
||||
@@ -69,7 +69,7 @@ test
|
||||
async (t) => {
|
||||
await t
|
||||
.useRole(roles.deleteObjectWithPrefixOnly)
|
||||
.navigateTo(`http://localhost:9090/buckets`)
|
||||
.navigateTo(`http://localhost:9090/browser`)
|
||||
.click(test2BucketBrowseButton)
|
||||
.click(
|
||||
Selector(".ReactVirtualized__Table__rowColumn").withText(
|
||||
@@ -103,7 +103,7 @@ test
|
||||
async (t) => {
|
||||
await t
|
||||
.useRole(roles.deleteObjectWithPrefixOnly)
|
||||
.navigateTo(`http://localhost:9090/buckets`)
|
||||
.navigateTo(`http://localhost:9090/browser`)
|
||||
.click(test3BucketBrowseButton)
|
||||
.click(
|
||||
Selector(".ReactVirtualized__Table__rowColumn").withText(
|
||||
|
||||
@@ -33,12 +33,12 @@ test
|
||||
await functions.setVersioned(t, "bucketdelete3");
|
||||
await t
|
||||
.useRole(roles.bucketReadWrite)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("bucketdelete3"))
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt")
|
||||
.wait(1000)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("bucketdelete3"))
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt")
|
||||
@@ -46,7 +46,7 @@ test
|
||||
})("All versions of an object can be deleted from a bucket", async (t) => {
|
||||
await t
|
||||
.useRole(roles.bucketReadWrite)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("bucketdelete3"))
|
||||
.click(
|
||||
"div.ReactVirtualized__Grid.ReactVirtualized__Table__Grid > div > div:nth-child(1)"
|
||||
|
||||
@@ -31,7 +31,7 @@ test
|
||||
await functions.setVersioned(t, "bucketobjecttags");
|
||||
await t
|
||||
.useRole(roles.bucketObjectTags)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("bucketobjecttags"))
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt")
|
||||
@@ -39,7 +39,7 @@ test
|
||||
})("Tags can be created and deleted", async (t) => {
|
||||
await t
|
||||
.useRole(roles.bucketObjectTags)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("bucketobjecttags"))
|
||||
.click(
|
||||
"div.ReactVirtualized__Grid.ReactVirtualized__Table__Grid > div > div:nth-child(1)"
|
||||
@@ -69,7 +69,7 @@ test
|
||||
await functions.setVersioned(t, "bucketcannottag");
|
||||
await t
|
||||
.useRole(roles.bucketCannotTag)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("bucketcannottag"))
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt")
|
||||
@@ -77,7 +77,7 @@ test
|
||||
})("User should not be able to create tag", async (t) => {
|
||||
await t
|
||||
.useRole(roles.bucketCannotTag)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("bucketcannottag"))
|
||||
.click(
|
||||
"div.ReactVirtualized__Grid.ReactVirtualized__Table__Grid > div > div:nth-child(1)"
|
||||
|
||||
@@ -41,7 +41,7 @@ test
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
await t
|
||||
.useRole(roles.bucketRead)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.expect(testBucketBrowseButtonFor("bucketread1").exists)
|
||||
.ok();
|
||||
})
|
||||
@@ -79,7 +79,7 @@ test
|
||||
await functions.setUpBucket(t, "aread3");
|
||||
await t
|
||||
.useRole(roles.admin)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("aread3"))
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt")
|
||||
@@ -88,7 +88,7 @@ test
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
await t
|
||||
.useRole(roles.bucketRead)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.wait(2000)
|
||||
.click(testBucketBrowseButtonFor("aread3"))
|
||||
.wait(2000)
|
||||
|
||||
@@ -117,7 +117,7 @@ test
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
await t
|
||||
.useRole(roles.bucketRead)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.expect(
|
||||
namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-4`).exists
|
||||
)
|
||||
@@ -157,7 +157,7 @@ test
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-6`);
|
||||
await t
|
||||
.useRole(roles.admin)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-6`))
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt")
|
||||
@@ -166,7 +166,7 @@ test
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
await t
|
||||
.useRole(roles.bucketRead)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-6`))
|
||||
.expect(elements.table.exists)
|
||||
.ok();
|
||||
@@ -191,7 +191,7 @@ test
|
||||
);
|
||||
await t
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.expect(testBucketBrowseButton.exists)
|
||||
.ok();
|
||||
})
|
||||
@@ -240,7 +240,7 @@ test
|
||||
);
|
||||
await t
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButton)
|
||||
.expect(uploadExists)
|
||||
.ok();
|
||||
@@ -263,7 +263,7 @@ test
|
||||
);
|
||||
await t
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButton)
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt");
|
||||
@@ -283,7 +283,7 @@ test
|
||||
})("Object list table is disabled", async (t) => {
|
||||
await t
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-11`))
|
||||
.expect(elements.bucketsTableDisabled.exists)
|
||||
.ok();
|
||||
|
||||
@@ -61,7 +61,7 @@ test
|
||||
async (t) => {
|
||||
await t
|
||||
.useRole(roles.conditions2)
|
||||
.navigateTo(`http://localhost:9090/buckets`)
|
||||
.navigateTo(`http://localhost:9090/browser`)
|
||||
.click(test1BucketBrowseButton)
|
||||
.click(
|
||||
Selector(".ReactVirtualized__Table__rowColumn").withText("firstlevel")
|
||||
@@ -111,7 +111,7 @@ test
|
||||
})("User can browse from first level as policy has wildcard", async (t) => {
|
||||
await t
|
||||
.useRole(roles.conditions1)
|
||||
.navigateTo(`http://localhost:9090/buckets`)
|
||||
.navigateTo(`http://localhost:9090/browser`)
|
||||
.click(test1BucketBrowseButton)
|
||||
.click(
|
||||
Selector(".ReactVirtualized__Table__rowColumn").withText("firstlevel")
|
||||
|
||||
@@ -28,12 +28,12 @@ test
|
||||
await functions.setVersioned(t, "abucketrewind");
|
||||
await t
|
||||
.useRole(roles.bucketReadWrite)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("abucketrewind"))
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt")
|
||||
.wait(1000)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("abucketrewind"))
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt")
|
||||
@@ -41,7 +41,7 @@ test
|
||||
})("Rewind works in bucket", async (t) => {
|
||||
await t
|
||||
.useRole(roles.bucketReadWrite)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.navigateTo("http://localhost:9090/browser")
|
||||
.click(testBucketBrowseButtonFor("abucketrewind"))
|
||||
.expect(elements.table.exists)
|
||||
.ok()
|
||||
|
||||
@@ -29,7 +29,7 @@ test
|
||||
const uploadButton = elements.uploadButton;
|
||||
await t
|
||||
.useRole(roles.bucketWritePrefixOnly)
|
||||
.navigateTo("http://localhost:9090/buckets/testcafe/browse")
|
||||
.navigateTo("http://localhost:9090/browser/testcafe")
|
||||
.click(uploadButton)
|
||||
.expect(Selector("li").withText("Upload File").hasClass("Mui-disabled"))
|
||||
.ok()
|
||||
@@ -48,7 +48,7 @@ test
|
||||
const uploadButton = elements.uploadButton;
|
||||
await t
|
||||
.useRole(roles.bucketWritePrefixOnly)
|
||||
.navigateTo("http://localhost:9090/buckets/testcafe/browse/d3JpdGU=")
|
||||
.navigateTo("http://localhost:9090/browser/testcafe/d3JpdGU=")
|
||||
.click(uploadButton)
|
||||
.expect(Selector("li").withText("Upload File").hasClass("Mui-disabled"))
|
||||
.notOk()
|
||||
|
||||
@@ -94,11 +94,7 @@ export const setVersionedBucket = (t, name) => {
|
||||
};
|
||||
|
||||
export const namedManageButtonFor = (name) => {
|
||||
return Selector("h1")
|
||||
.withText(name)
|
||||
.parent(4)
|
||||
.find("button:enabled")
|
||||
.withText("Manage");
|
||||
return Selector("div").withAttribute("id", `manageBucket-${name}`);
|
||||
};
|
||||
|
||||
export const manageButtonFor = (modifier) => {
|
||||
@@ -122,9 +118,7 @@ export const cleanUpBucket = (t, modifier) => {
|
||||
};
|
||||
|
||||
export const namedTestBucketBrowseButtonFor = (name) => {
|
||||
return Selector("button:enabled")
|
||||
.withAttribute("id", `browse-${name}`)
|
||||
.withText("Browse");
|
||||
return Selector("span").withAttribute("id", `browse-${name}`);
|
||||
};
|
||||
|
||||
export const testBucketBrowseButtonFor = (modifier) => {
|
||||
|
||||
@@ -2308,11 +2308,6 @@
|
||||
dependencies:
|
||||
"@types/unist" "*"
|
||||
|
||||
"@types/history@^4.7.11":
|
||||
version "4.7.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
|
||||
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
|
||||
|
||||
"@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||
@@ -2487,23 +2482,6 @@
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
redux "^4.0.0"
|
||||
|
||||
"@types/react-router-dom@^5.3.3":
|
||||
version "5.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
|
||||
integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
|
||||
dependencies:
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
"@types/react-router" "*"
|
||||
|
||||
"@types/react-router@*":
|
||||
version "5.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.18.tgz#c8851884b60bc23733500d86c1266e1cfbbd9ef3"
|
||||
integrity sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g==
|
||||
dependencies:
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-transition-group@^4.4.5":
|
||||
version "4.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
|
||||
@@ -5929,10 +5907,10 @@ forwarded@0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
||||
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
|
||||
|
||||
fp-ts@^2.12.1:
|
||||
version "2.12.2"
|
||||
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.12.2.tgz#a191db2dbbb04f48a0e75050b94f57cc876c7b40"
|
||||
integrity sha512-v8J7ud+nTkP5Zz17GhpCsY19wiRbB9miuj61nBcCJyDpu52zs9Z4O7OLDfYoKFQMJ9EsSZA7W1vRgC1d3jy5qw==
|
||||
fp-ts@^2.9.5:
|
||||
version "2.13.1"
|
||||
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.13.1.tgz#1bf2b24136cca154846af16752dc29e8fa506f2a"
|
||||
integrity sha512-0eu5ULPS2c/jsa1lGFneEFFEdTbembJv8e4QKXeVJ3lm/5hyve06dlKZrpxmMwJt6rYen7sxmHHK2CLaXvWuWQ==
|
||||
|
||||
fraction.js@^4.2.0:
|
||||
version "4.2.0"
|
||||
@@ -6507,6 +6485,11 @@ http-proxy@^1.18.1:
|
||||
follow-redirects "^1.0.0"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
http-status-codes@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.2.0.tgz#bb2efe63d941dfc2be18e15f703da525169622be"
|
||||
integrity sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng==
|
||||
|
||||
https-proxy-agent@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
||||
@@ -6911,6 +6894,11 @@ is-plain-obj@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
|
||||
integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
|
||||
|
||||
is-podman@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-podman/-/is-podman-1.0.1.tgz#284a7ba1e6987fff8af5d71d97a29d2a85b7996a"
|
||||
integrity sha512-+5vbtF5FIg262iUa7gOIseIWTx0740RHiax7oSmJMhbfSoBIMQ/IacKKgfnGj65JGeH9lGEVQcdkDwhn1Em1mQ==
|
||||
|
||||
is-potential-custom-element-name@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
|
||||
@@ -11108,10 +11096,10 @@ testcafe-browser-tools@2.0.23:
|
||||
read-file-relative "^1.2.0"
|
||||
which-promise "^1.0.0"
|
||||
|
||||
testcafe-hammerhead@24.7.2:
|
||||
version "24.7.2"
|
||||
resolved "https://registry.yarnpkg.com/testcafe-hammerhead/-/testcafe-hammerhead-24.7.2.tgz#e3665ead7b1df63593fc278f9345f8fbec815147"
|
||||
integrity sha512-f9f/CuOtaeIq+avD8hFO6aMGCdc446R1+7h3TR+4vuqkOQaqow5SuwEI/QdCF05z8nENDcXGUORXx0hOPrGhlw==
|
||||
testcafe-hammerhead@28.1.0:
|
||||
version "28.1.0"
|
||||
resolved "https://registry.yarnpkg.com/testcafe-hammerhead/-/testcafe-hammerhead-28.1.0.tgz#fcfc620730caaa42514854256a19b207059fc475"
|
||||
integrity sha512-6J+U1MEV8L7OI467tkpOpewqYE6u0bhDTGQhK1s8cvDZtcvzm1ArFTRJvyG1wMb3/QaGrzoCHpEMRJ2BIK/0lw==
|
||||
dependencies:
|
||||
acorn-hammerhead "0.6.1"
|
||||
asar "^2.0.1"
|
||||
@@ -11168,10 +11156,10 @@ testcafe-hammerhead@>=19.4.0:
|
||||
tunnel-agent "0.6.0"
|
||||
webauth "^1.1.0"
|
||||
|
||||
testcafe-legacy-api@5.1.4:
|
||||
version "5.1.4"
|
||||
resolved "https://registry.yarnpkg.com/testcafe-legacy-api/-/testcafe-legacy-api-5.1.4.tgz#de913a79869abf9c5ff117eeb9adbef78519f4ff"
|
||||
integrity sha512-CWjwGlRZdSuoWDIRBHKetpmDffR+/LKS6+69n8VM4mkLKgUwsP8p3MERHdx0obBn8wZ0LSyrYj8SCtv5f7oWZg==
|
||||
testcafe-legacy-api@5.1.6:
|
||||
version "5.1.6"
|
||||
resolved "https://registry.yarnpkg.com/testcafe-legacy-api/-/testcafe-legacy-api-5.1.6.tgz#157b29902153cc086649f91960a4e45694481f50"
|
||||
integrity sha512-Q451IdSUX1NmRfE8kzIcEeoqbUlLaMv2fwVNgQOBEFmA5E57c3jsIpLDTDqv6FPcNwdNMYIZMiB6tzlXB5wf1g==
|
||||
dependencies:
|
||||
async "3.2.3"
|
||||
dedent "^0.6.0"
|
||||
@@ -11188,13 +11176,13 @@ testcafe-legacy-api@5.1.4:
|
||||
strip-bom "^2.0.0"
|
||||
testcafe-hammerhead ">=19.4.0"
|
||||
|
||||
testcafe-reporter-dashboard@1.0.0-rc.3:
|
||||
version "1.0.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/testcafe-reporter-dashboard/-/testcafe-reporter-dashboard-1.0.0-rc.3.tgz#9065f7265ea2d63c171f867f23b6d8d9e5d9f1ae"
|
||||
integrity sha512-F4gphX9/KlZzEz26I9LwUw7DKdKFQEpsU4Pr04ssBOJCyZh4ST7kuOyB+JMqr4iJfE9zu6+g6aaXumM+ad61JA==
|
||||
testcafe-reporter-dashboard@^0.2.7:
|
||||
version "0.2.8"
|
||||
resolved "https://registry.yarnpkg.com/testcafe-reporter-dashboard/-/testcafe-reporter-dashboard-0.2.8.tgz#66d14a9cffc7d98a56ea59f22e69aa01f6c18ac7"
|
||||
integrity sha512-C/uTJoLgCWkBRlO6jaHDmHC4qAncZut/05s+gxPiFsPNKMZnbgAFEfzJHWOgt6B4eWbREruvuyH99Lr5xXo4wQ==
|
||||
dependencies:
|
||||
es6-promise "^4.2.8"
|
||||
fp-ts "^2.12.1"
|
||||
fp-ts "^2.9.5"
|
||||
io-ts "^2.2.14"
|
||||
io-ts-types "^0.5.15"
|
||||
isomorphic-fetch "^3.0.0"
|
||||
@@ -11202,7 +11190,7 @@ testcafe-reporter-dashboard@1.0.0-rc.3:
|
||||
monocle-ts "^2.3.5"
|
||||
newtype-ts "^0.3.4"
|
||||
semver "^5.6.0"
|
||||
uuid "3.3.3"
|
||||
uuid "^9.0.0"
|
||||
|
||||
testcafe-reporter-json@^2.1.0:
|
||||
version "2.2.0"
|
||||
@@ -11234,10 +11222,10 @@ testcafe-safe-storage@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/testcafe-safe-storage/-/testcafe-safe-storage-1.1.2.tgz#dacfda9a51c77f61f11b13506d4004dd7f27eb73"
|
||||
integrity sha512-6km7D26+KCQGeFlSQ9fVEU7tD8qdioSpqzxU8CCZkd2KzBS0jTFkqaJ54rPaLdd5+wdhgO3+as3LMm4F0EDeYA==
|
||||
|
||||
testcafe@^1.18.6:
|
||||
version "1.20.1"
|
||||
resolved "https://registry.yarnpkg.com/testcafe/-/testcafe-1.20.1.tgz#872265fb0dc943bd15da7f4cfaa2162df61e36e3"
|
||||
integrity sha512-D5UQsR10zsqPSqN4uWxS8CjmpaRUSduo3hjQscIzww8/uZ3AmOYJOrIk+punpieCmcwclfGz6ic2pdRv21rNnA==
|
||||
testcafe@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/testcafe/-/testcafe-2.1.0.tgz#382f1e8d62072ee02caf619bb43492d22d5d5a99"
|
||||
integrity sha512-mfF9UX2U9RyjNfuviaOf/MYEQA7uGxe0n+VXJzdPMqpPjvILe/LrAopyxjkmW5MllZAGW0/tmwOy9xRZl50l7A==
|
||||
dependencies:
|
||||
"@babel/core" "^7.12.1"
|
||||
"@babel/plugin-proposal-async-generator-functions" "^7.12.1"
|
||||
@@ -11284,12 +11272,14 @@ testcafe@^1.18.6:
|
||||
globby "^11.0.4"
|
||||
graceful-fs "^4.1.11"
|
||||
graphlib "^2.1.5"
|
||||
http-status-codes "^2.2.0"
|
||||
humanize-duration "^3.25.0"
|
||||
import-lazy "^3.1.0"
|
||||
indent-string "^1.2.2"
|
||||
is-ci "^1.0.10"
|
||||
is-docker "^2.0.0"
|
||||
is-glob "^2.0.1"
|
||||
is-podman "^1.0.1"
|
||||
is-stream "^2.0.0"
|
||||
json5 "^2.1.0"
|
||||
lodash "^4.17.13"
|
||||
@@ -11318,9 +11308,9 @@ testcafe@^1.18.6:
|
||||
source-map-support "^0.5.16"
|
||||
strip-bom "^2.0.0"
|
||||
testcafe-browser-tools "2.0.23"
|
||||
testcafe-hammerhead "24.7.2"
|
||||
testcafe-legacy-api "5.1.4"
|
||||
testcafe-reporter-dashboard "1.0.0-rc.3"
|
||||
testcafe-hammerhead "28.1.0"
|
||||
testcafe-legacy-api "5.1.6"
|
||||
testcafe-reporter-dashboard "^0.2.7"
|
||||
testcafe-reporter-json "^2.1.0"
|
||||
testcafe-reporter-list "^2.1.0"
|
||||
testcafe-reporter-minimal "^2.1.0"
|
||||
@@ -11330,7 +11320,7 @@ testcafe@^1.18.6:
|
||||
time-limit-promise "^1.0.2"
|
||||
tmp "0.0.28"
|
||||
tree-kill "^1.2.2"
|
||||
typescript "^3.3.3"
|
||||
typescript "4.7.4"
|
||||
unquote "^1.1.1"
|
||||
|
||||
text-segmentation@^1.0.3:
|
||||
@@ -11585,10 +11575,10 @@ typedarray-to-buffer@^3.1.5:
|
||||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typescript@^3.3.3:
|
||||
version "3.9.10"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8"
|
||||
integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==
|
||||
typescript@4.7.4:
|
||||
version "4.7.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
|
||||
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
|
||||
|
||||
typescript@^4.4.3:
|
||||
version "4.8.2"
|
||||
@@ -11805,16 +11795,16 @@ utrie@^1.0.2:
|
||||
dependencies:
|
||||
base64-arraybuffer "^1.0.2"
|
||||
|
||||
uuid@3.3.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
|
||||
integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==
|
||||
|
||||
uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
uuid@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
|
||||
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
|
||||
|
||||
v8-to-istanbul@^8.1.0:
|
||||
version "8.1.1"
|
||||
resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed"
|
||||
|
||||
@@ -43,6 +43,7 @@ type WSResponse struct {
|
||||
RequestID int64 `json:"request_id,nonempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
RequestEnd bool `json:"request_end,omitempty"`
|
||||
Prefix string `json:"prefix,omitempty"`
|
||||
Data []ObjectResponse `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -617,6 +617,7 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
|
||||
writeChannel <- WSResponse{
|
||||
RequestID: messageRequest.RequestID,
|
||||
Error: lsObj.Err.Error(),
|
||||
Prefix: messageRequest.Prefix,
|
||||
}
|
||||
|
||||
continue
|
||||
@@ -689,6 +690,7 @@ func (wsc *wsMinioClient) objectManager(session *models.Principal) {
|
||||
writeChannel <- WSResponse{
|
||||
RequestID: messageRequest.RequestID,
|
||||
Error: lsObj.Err.String(),
|
||||
Prefix: messageRequest.Prefix,
|
||||
}
|
||||
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user