mirror of
https://github.com/minio/minio.git
synced 2025-04-09 06:00:12 -04:00
allow non-loggedin users to access public bucket (#5570)
* conditionally render main action buttons - Make bucket action will be available only for loggedIn users - File upload button will be avaialble for loggedIn users and non-loggedIn users if the prefix is writable * select the bucket and prefix from the url When the url contains bucket and prefix, it will be selected by default instead of the first bucket from the list. * show BucketSearch only for LoggedIn users * allow non-LoggedIn users to access public bucket * removed unused Router imports * fix test case failures in BucketList.test.js * remove dupicate minioBrowserPrefix from url since history is already initialized with minioBrowserPrefix, no need to use it in push or replace * remove unused match from App component * remove unused minioBrowserPrefix imports
This commit is contained in:
parent
bb0adea494
commit
416841869a
@ -21,10 +21,10 @@ import "material-design-iconic-font/dist/css/material-design-iconic-font.min.css
|
|||||||
|
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import ReactDOM from "react-dom"
|
import ReactDOM from "react-dom"
|
||||||
import { BrowserRouter, Route } from "react-router-dom"
|
import { Router, Route } from "react-router-dom"
|
||||||
import { Provider } from "react-redux"
|
import { Provider } from "react-redux"
|
||||||
|
|
||||||
import { minioBrowserPrefix } from "./js/constants"
|
import history from "./js/history"
|
||||||
import configureStore from "./js/store/configure-store"
|
import configureStore from "./js/store/configure-store"
|
||||||
import hideLoader from "./js/loader"
|
import hideLoader from "./js/loader"
|
||||||
import App from "./js/App"
|
import App from "./js/App"
|
||||||
@ -33,9 +33,9 @@ const store = configureStore()
|
|||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<BrowserRouter>
|
<Router history={history}>
|
||||||
<Route path={minioBrowserPrefix} component={App} />
|
<App />
|
||||||
</BrowserRouter>
|
</Router>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById("root")
|
document.getElementById("root")
|
||||||
)
|
)
|
||||||
|
@ -34,13 +34,15 @@ const AuthorizedRoute = ({ component: Component, ...rest }) => (
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
export const App = ({ match }) => (
|
export const App = () => {
|
||||||
<Switch>
|
return (
|
||||||
<AuthorizedRoute exact path={match.url} component={Browser} />
|
<Switch>
|
||||||
<Route path={`${match.url}/login`} component={Login} />
|
<AuthorizedRoute exact path={"/"} component={Browser} />
|
||||||
<AuthorizedRoute path={`${match.url}/:bucket/*`} component={Browser} />
|
<Route path={"/login"} component={Login} />
|
||||||
<AuthorizedRoute path={`${match.url}/:bucket`} component={Browser} />
|
<Route path={"/:bucket/*"} component={Browser} />
|
||||||
</Switch>
|
<Route path={"/:bucket"} component={Browser} />
|
||||||
)
|
</Switch>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default App
|
export default App
|
||||||
|
@ -21,7 +21,6 @@ import logo from "../../img/logo.svg"
|
|||||||
import Alert from "../alert/Alert"
|
import Alert from "../alert/Alert"
|
||||||
import * as actionsAlert from "../alert/actions"
|
import * as actionsAlert from "../alert/actions"
|
||||||
import InputGroup from "./InputGroup"
|
import InputGroup from "./InputGroup"
|
||||||
import { minioBrowserPrefix } from "../constants"
|
|
||||||
import web from "../web"
|
import web from "../web"
|
||||||
import { Redirect } from "react-router-dom"
|
import { Redirect } from "react-router-dom"
|
||||||
|
|
||||||
@ -46,7 +45,7 @@ export class Login extends React.Component {
|
|||||||
password: document.getElementById("secretKey").value
|
password: document.getElementById("secretKey").value
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
history.push(minioBrowserPrefix)
|
history.push("/")
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
showAlert("danger", e.message)
|
showAlert("danger", e.message)
|
||||||
@ -67,7 +66,7 @@ export class Login extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
const { clearAlert, alert } = this.props
|
const { clearAlert, alert } = this.props
|
||||||
if (web.LoggedIn()) {
|
if (web.LoggedIn()) {
|
||||||
return <Redirect to={minioBrowserPrefix} />
|
return <Redirect to={"/"} />
|
||||||
}
|
}
|
||||||
let alertBox = <Alert {...alert} onDismiss={clearAlert} />
|
let alertBox = <Alert {...alert} onDismiss={clearAlert} />
|
||||||
// Make sure you don't show a fading out alert box on the initial web-page load.
|
// Make sure you don't show a fading out alert box on the initial web-page load.
|
||||||
|
@ -17,10 +17,16 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { connect } from "react-redux"
|
import { connect } from "react-redux"
|
||||||
import { Dropdown, OverlayTrigger, Tooltip } from "react-bootstrap"
|
import { Dropdown, OverlayTrigger, Tooltip } from "react-bootstrap"
|
||||||
|
import web from "../web"
|
||||||
import * as actionsBuckets from "../buckets/actions"
|
import * as actionsBuckets from "../buckets/actions"
|
||||||
import * as uploadsActions from "../uploads/actions"
|
import * as uploadsActions from "../uploads/actions"
|
||||||
|
import { getPrefixWritable } from "../objects/selectors"
|
||||||
|
|
||||||
export const MainActions = ({ uploadFile, showMakeBucketModal }) => {
|
export const MainActions = ({
|
||||||
|
prefixWritable,
|
||||||
|
uploadFile,
|
||||||
|
showMakeBucketModal
|
||||||
|
}) => {
|
||||||
const uploadTooltip = <Tooltip id="tt-upload-file">Upload file</Tooltip>
|
const uploadTooltip = <Tooltip id="tt-upload-file">Upload file</Tooltip>
|
||||||
const makeBucketTooltip = (
|
const makeBucketTooltip = (
|
||||||
<Tooltip id="tt-create-bucket">Create bucket</Tooltip>
|
<Tooltip id="tt-create-bucket">Create bucket</Tooltip>
|
||||||
@ -31,47 +37,59 @@ export const MainActions = ({ uploadFile, showMakeBucketModal }) => {
|
|||||||
e.target.value = null
|
e.target.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const loggedIn = web.LoggedIn()
|
||||||
<Dropdown dropup className="feb-actions" id="fe-action-toggle">
|
|
||||||
<Dropdown.Toggle noCaret className="feba-toggle">
|
if (loggedIn || prefixWritable) {
|
||||||
<span>
|
return (
|
||||||
<i className="fa fa-plus" />
|
<Dropdown dropup className="feb-actions" id="fe-action-toggle">
|
||||||
</span>
|
<Dropdown.Toggle noCaret className="feba-toggle">
|
||||||
</Dropdown.Toggle>
|
<span>
|
||||||
<Dropdown.Menu>
|
<i className="fa fa-plus" />
|
||||||
<OverlayTrigger placement="left" overlay={uploadTooltip}>
|
</span>
|
||||||
<a href="#" className="feba-btn feba-upload">
|
</Dropdown.Toggle>
|
||||||
<input
|
<Dropdown.Menu>
|
||||||
type="file"
|
<OverlayTrigger placement="left" overlay={uploadTooltip}>
|
||||||
onChange={onFileUpload}
|
<a href="#" className="feba-btn feba-upload">
|
||||||
style={{ display: "none" }}
|
<input
|
||||||
id="file-input"
|
type="file"
|
||||||
/>
|
onChange={onFileUpload}
|
||||||
<label htmlFor="file-input">
|
style={{ display: "none" }}
|
||||||
{" "}
|
id="file-input"
|
||||||
<i className="fa fa-cloud-upload" />{" "}
|
/>
|
||||||
</label>
|
<label htmlFor="file-input">
|
||||||
</a>
|
{" "}
|
||||||
</OverlayTrigger>
|
<i className="fa fa-cloud-upload" />{" "}
|
||||||
<OverlayTrigger placement="left" overlay={makeBucketTooltip}>
|
</label>
|
||||||
<a
|
</a>
|
||||||
href="#"
|
</OverlayTrigger>
|
||||||
id="show-make-bucket"
|
{loggedIn && (
|
||||||
className="feba-btn feba-bucket"
|
<OverlayTrigger placement="left" overlay={makeBucketTooltip}>
|
||||||
onClick={e => {
|
<a
|
||||||
e.preventDefault()
|
href="#"
|
||||||
showMakeBucketModal()
|
id="show-make-bucket"
|
||||||
}}
|
className="feba-btn feba-bucket"
|
||||||
>
|
onClick={e => {
|
||||||
<i className="fa fa-hdd-o" />
|
e.preventDefault()
|
||||||
</a>
|
showMakeBucketModal()
|
||||||
</OverlayTrigger>
|
}}
|
||||||
</Dropdown.Menu>
|
>
|
||||||
</Dropdown>
|
<i className="fa fa-hdd-o" />
|
||||||
)
|
</a>
|
||||||
|
</OverlayTrigger>
|
||||||
|
)}
|
||||||
|
</Dropdown.Menu>
|
||||||
|
</Dropdown>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return <noscript />
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => state
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
prefixWritable: getPrefixWritable(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
|
@ -25,6 +25,7 @@ import BucketSearch from "../buckets/BucketSearch"
|
|||||||
import BucketList from "../buckets/BucketList"
|
import BucketList from "../buckets/BucketList"
|
||||||
import Host from "./Host"
|
import Host from "./Host"
|
||||||
import * as actionsCommon from "./actions"
|
import * as actionsCommon from "./actions"
|
||||||
|
import web from "../web"
|
||||||
|
|
||||||
export const SideBar = ({ sidebarOpen, clickOutside }) => {
|
export const SideBar = ({ sidebarOpen, clickOutside }) => {
|
||||||
return (
|
return (
|
||||||
@ -40,7 +41,7 @@ export const SideBar = ({ sidebarOpen, clickOutside }) => {
|
|||||||
<h2>Minio Browser</h2>
|
<h2>Minio Browser</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="fes-list">
|
<div className="fes-list">
|
||||||
<BucketSearch />
|
{web.LoggedIn() && <BucketSearch />}
|
||||||
<BucketList />
|
<BucketList />
|
||||||
</div>
|
</div>
|
||||||
<Host />
|
<Host />
|
||||||
|
@ -18,11 +18,37 @@ import React from "react"
|
|||||||
import { shallow, mount } from "enzyme"
|
import { shallow, mount } from "enzyme"
|
||||||
import { MainActions } from "../MainActions"
|
import { MainActions } from "../MainActions"
|
||||||
|
|
||||||
|
jest.mock("../../web", () => ({
|
||||||
|
LoggedIn: jest
|
||||||
|
.fn(() => true)
|
||||||
|
.mockReturnValueOnce(true)
|
||||||
|
.mockReturnValueOnce(false)
|
||||||
|
.mockReturnValueOnce(false)
|
||||||
|
}))
|
||||||
|
|
||||||
describe("MainActions", () => {
|
describe("MainActions", () => {
|
||||||
it("should render without crashing", () => {
|
it("should render without crashing", () => {
|
||||||
shallow(<MainActions />)
|
shallow(<MainActions />)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should not show any actions when user has not LoggedIn and prefixWritable is false", () => {
|
||||||
|
const wrapper = shallow(<MainActions />)
|
||||||
|
expect(wrapper.find("#show-make-bucket").length).toBe(0)
|
||||||
|
expect(wrapper.find("#file-input").length).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should show only file upload action when user has not LoggedIn and prefixWritable is true", () => {
|
||||||
|
const wrapper = shallow(<MainActions prefixWritable={true} />)
|
||||||
|
expect(wrapper.find("#show-make-bucket").length).toBe(0)
|
||||||
|
expect(wrapper.find("#file-input").length).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should show make bucket upload file actions when user has LoggedIn", () => {
|
||||||
|
const wrapper = shallow(<MainActions />)
|
||||||
|
expect(wrapper.find("#show-make-bucket").length).toBe(1)
|
||||||
|
expect(wrapper.find("#file-input").length).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
it("should call showMakeBucketModal when create bucket icon is clicked", () => {
|
it("should call showMakeBucketModal when create bucket icon is clicked", () => {
|
||||||
const showMakeBucketModal = jest.fn()
|
const showMakeBucketModal = jest.fn()
|
||||||
const wrapper = shallow(
|
const wrapper = shallow(
|
||||||
|
@ -18,11 +18,20 @@ import React from "react"
|
|||||||
import { shallow } from "enzyme"
|
import { shallow } from "enzyme"
|
||||||
import { SideBar } from "../SideBar"
|
import { SideBar } from "../SideBar"
|
||||||
|
|
||||||
|
jest.mock("../../web", () => ({
|
||||||
|
LoggedIn: jest.fn(() => false).mockReturnValueOnce(true)
|
||||||
|
}))
|
||||||
|
|
||||||
describe("SideBar", () => {
|
describe("SideBar", () => {
|
||||||
it("should render without crashing", () => {
|
it("should render without crashing", () => {
|
||||||
shallow(<SideBar />)
|
shallow(<SideBar />)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should not render BucketSearch for non LoggedIn users", () => {
|
||||||
|
const wrapper = shallow(<SideBar />)
|
||||||
|
expect(wrapper.find("Connect(BucketSearch)").length).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
it("should call clickOutside when the user clicks outside the sidebar", () => {
|
it("should call clickOutside when the user clicks outside the sidebar", () => {
|
||||||
const clickOutside = jest.fn()
|
const clickOutside = jest.fn()
|
||||||
const wrapper = shallow(<SideBar clickOutside={clickOutside} />)
|
const wrapper = shallow(<SideBar clickOutside={clickOutside} />)
|
||||||
|
@ -20,11 +20,22 @@ import { Scrollbars } from "react-custom-scrollbars"
|
|||||||
import * as actionsBuckets from "./actions"
|
import * as actionsBuckets from "./actions"
|
||||||
import { getVisibleBuckets } from "./selectors"
|
import { getVisibleBuckets } from "./selectors"
|
||||||
import BucketContainer from "./BucketContainer"
|
import BucketContainer from "./BucketContainer"
|
||||||
|
import web from "../web"
|
||||||
|
import history from "../history"
|
||||||
|
import { pathSlice } from "../utils"
|
||||||
|
|
||||||
export class BucketList extends React.Component {
|
export class BucketList extends React.Component {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const { fetchBuckets } = this.props
|
const { fetchBuckets, setBucketList, selectBucket } = this.props
|
||||||
fetchBuckets()
|
if (web.LoggedIn()) {
|
||||||
|
fetchBuckets()
|
||||||
|
} else {
|
||||||
|
const { bucket, prefix } = pathSlice(history.location.pathname)
|
||||||
|
if (bucket) {
|
||||||
|
setBucketList([bucket])
|
||||||
|
selectBucket(bucket, prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
const { visibleBuckets } = this.props
|
const { visibleBuckets } = this.props
|
||||||
@ -52,7 +63,9 @@ const mapStateToProps = state => {
|
|||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
fetchBuckets: () => dispatch(actionsBuckets.fetchBuckets())
|
fetchBuckets: () => dispatch(actionsBuckets.fetchBuckets()),
|
||||||
|
setBucketList: buckets => dispatch(actionsBuckets.setList(buckets)),
|
||||||
|
selectBucket: bucket => dispatch(actionsBuckets.selectBucket(bucket))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,8 +16,16 @@
|
|||||||
|
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { shallow } from "enzyme"
|
import { shallow } from "enzyme"
|
||||||
|
import history from "../../history"
|
||||||
import { BucketList } from "../BucketList"
|
import { BucketList } from "../BucketList"
|
||||||
|
|
||||||
|
jest.mock("../../web", () => ({
|
||||||
|
LoggedIn: jest
|
||||||
|
.fn(() => false)
|
||||||
|
.mockReturnValueOnce(true)
|
||||||
|
.mockReturnValueOnce(true)
|
||||||
|
}))
|
||||||
|
|
||||||
describe("BucketList", () => {
|
describe("BucketList", () => {
|
||||||
it("should render without crashing", () => {
|
it("should render without crashing", () => {
|
||||||
const fetchBuckets = jest.fn()
|
const fetchBuckets = jest.fn()
|
||||||
@ -31,4 +39,19 @@ describe("BucketList", () => {
|
|||||||
)
|
)
|
||||||
expect(fetchBuckets).toHaveBeenCalled()
|
expect(fetchBuckets).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should call setBucketList and selectBucket before component is mounted when the user has not loggedIn", () => {
|
||||||
|
const setBucketList = jest.fn()
|
||||||
|
const selectBucket = jest.fn()
|
||||||
|
history.push("/bk1/pre1")
|
||||||
|
const wrapper = shallow(
|
||||||
|
<BucketList
|
||||||
|
visibleBuckets={[]}
|
||||||
|
setBucketList={setBucketList}
|
||||||
|
selectBucket={selectBucket}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
expect(setBucketList).toHaveBeenCalledWith(["bk1"])
|
||||||
|
expect(selectBucket).toHaveBeenCalledWith("bk1", "pre1")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -18,6 +18,7 @@ import configureStore from "redux-mock-store"
|
|||||||
import thunk from "redux-thunk"
|
import thunk from "redux-thunk"
|
||||||
import * as actionsBuckets from "../actions"
|
import * as actionsBuckets from "../actions"
|
||||||
import * as objectActions from "../../objects/actions"
|
import * as objectActions from "../../objects/actions"
|
||||||
|
import history from "../../history"
|
||||||
|
|
||||||
jest.mock("../../web", () => ({
|
jest.mock("../../web", () => ({
|
||||||
ListBuckets: jest.fn(() => {
|
ListBuckets: jest.fn(() => {
|
||||||
@ -36,7 +37,7 @@ const middlewares = [thunk]
|
|||||||
const mockStore = configureStore(middlewares)
|
const mockStore = configureStore(middlewares)
|
||||||
|
|
||||||
describe("Buckets actions", () => {
|
describe("Buckets actions", () => {
|
||||||
it("creates buckets/SET_LIST and buckets/SET_CURRENT_BUCKET after fetching the buckets", () => {
|
it("creates buckets/SET_LIST and buckets/SET_CURRENT_BUCKET with first bucket after fetching the buckets", () => {
|
||||||
const store = mockStore()
|
const store = mockStore()
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: "buckets/SET_LIST", buckets: ["test1", "test2"] },
|
{ type: "buckets/SET_LIST", buckets: ["test1", "test2"] },
|
||||||
@ -48,7 +49,35 @@ describe("Buckets actions", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should update browser url and creates buckets/SET_CURRENT_BUCKET action when selectBucket is called", () => {
|
it("creates buckets/SET_CURRENT_BUCKET with bucket name in the url after fetching buckets", () => {
|
||||||
|
history.push("/test2")
|
||||||
|
const store = mockStore()
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: "buckets/SET_LIST", buckets: ["test1", "test2"] },
|
||||||
|
{ type: "buckets/SET_CURRENT_BUCKET", bucket: "test2" }
|
||||||
|
]
|
||||||
|
window.location
|
||||||
|
return store.dispatch(actionsBuckets.fetchBuckets()).then(() => {
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates buckets/SET_CURRENT_BUCKET with first bucket when the bucket in url is not exists after fetching buckets", () => {
|
||||||
|
history.push("/test3")
|
||||||
|
const store = mockStore()
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: "buckets/SET_LIST", buckets: ["test1", "test2"] },
|
||||||
|
{ type: "buckets/SET_CURRENT_BUCKET", bucket: "test1" }
|
||||||
|
]
|
||||||
|
window.location
|
||||||
|
return store.dispatch(actionsBuckets.fetchBuckets()).then(() => {
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates buckets/SET_CURRENT_BUCKET action when selectBucket is called", () => {
|
||||||
const store = mockStore()
|
const store = mockStore()
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: "buckets/SET_CURRENT_BUCKET", bucket: "test1" }
|
{ type: "buckets/SET_CURRENT_BUCKET", bucket: "test1" }
|
||||||
@ -56,7 +85,6 @@ describe("Buckets actions", () => {
|
|||||||
store.dispatch(actionsBuckets.selectBucket("test1"))
|
store.dispatch(actionsBuckets.selectBucket("test1"))
|
||||||
const actions = store.getActions()
|
const actions = store.getActions()
|
||||||
expect(actions).toEqual(expectedActions)
|
expect(actions).toEqual(expectedActions)
|
||||||
expect(window.location.pathname).toBe("/test1")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("creates buckets/SHOW_MAKE_BUCKET_MODAL for showMakeBucketModal", () => {
|
it("creates buckets/SHOW_MAKE_BUCKET_MODAL for showMakeBucketModal", () => {
|
||||||
|
@ -18,6 +18,7 @@ import web from "../web"
|
|||||||
import history from "../history"
|
import history from "../history"
|
||||||
import * as alertActions from "../alert/actions"
|
import * as alertActions from "../alert/actions"
|
||||||
import * as objectsActions from "../objects/actions"
|
import * as objectsActions from "../objects/actions"
|
||||||
|
import { pathSlice } from "../utils"
|
||||||
|
|
||||||
export const SET_LIST = "buckets/SET_LIST"
|
export const SET_LIST = "buckets/SET_LIST"
|
||||||
export const ADD = "buckets/ADD"
|
export const ADD = "buckets/ADD"
|
||||||
@ -31,7 +32,12 @@ export const fetchBuckets = () => {
|
|||||||
const buckets = res.buckets ? res.buckets.map(bucket => bucket.name) : []
|
const buckets = res.buckets ? res.buckets.map(bucket => bucket.name) : []
|
||||||
dispatch(setList(buckets))
|
dispatch(setList(buckets))
|
||||||
if (buckets.length > 0) {
|
if (buckets.length > 0) {
|
||||||
dispatch(selectBucket(buckets[0]))
|
const { bucket, prefix } = pathSlice(history.location.pathname)
|
||||||
|
if (bucket && buckets.indexOf(bucket) > -1) {
|
||||||
|
dispatch(selectBucket(bucket, prefix))
|
||||||
|
} else {
|
||||||
|
dispatch(selectBucket(buckets[0]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -51,11 +57,10 @@ export const setFilter = filter => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const selectBucket = bucket => {
|
export const selectBucket = (bucket, prefix) => {
|
||||||
return function(dispatch) {
|
return function(dispatch) {
|
||||||
dispatch(setCurrentBucket(bucket))
|
dispatch(setCurrentBucket(bucket))
|
||||||
dispatch(objectsActions.selectPrefix(""))
|
dispatch(objectsActions.selectPrefix(prefix || ""))
|
||||||
history.push(`/${bucket}`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,8 @@ jest.mock("../../web", () => ({
|
|||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
objects: [{ name: "test1" }, { name: "test2" }],
|
objects: [{ name: "test1" }, { name: "test2" }],
|
||||||
istruncated: false,
|
istruncated: false,
|
||||||
nextmarker: "test2"
|
nextmarker: "test2",
|
||||||
|
writable: false
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
RemoveObject: jest.fn(({ bucketName, objects }) => {
|
RemoveObject: jest.fn(({ bucketName, objects }) => {
|
||||||
@ -124,6 +125,10 @@ describe("Objects actions", () => {
|
|||||||
{
|
{
|
||||||
type: "objects/SET_SORT_ORDER",
|
type: "objects/SET_SORT_ORDER",
|
||||||
sortOrder: false
|
sortOrder: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "objects/SET_PREFIX_WRITABLE",
|
||||||
|
prefixWritable: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
return store.dispatch(actionsObjects.fetchObjects()).then(() => {
|
return store.dispatch(actionsObjects.fetchObjects()).then(() => {
|
||||||
@ -143,6 +148,10 @@ describe("Objects actions", () => {
|
|||||||
objects: [{ name: "test1" }, { name: "test2" }],
|
objects: [{ name: "test1" }, { name: "test2" }],
|
||||||
marker: "test2",
|
marker: "test2",
|
||||||
isTruncated: false
|
isTruncated: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "objects/SET_PREFIX_WRITABLE",
|
||||||
|
prefixWritable: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
return store.dispatch(actionsObjects.fetchObjects(true)).then(() => {
|
return store.dispatch(actionsObjects.fetchObjects(true)).then(() => {
|
||||||
@ -197,6 +206,16 @@ describe("Objects actions", () => {
|
|||||||
expect(window.location.pathname.endsWith("/test/abc/")).toBeTruthy()
|
expect(window.location.pathname.endsWith("/test/abc/")).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("create objects/SET_PREFIX_WRITABLE action", () => {
|
||||||
|
const store = mockStore()
|
||||||
|
const expectedActions = [
|
||||||
|
{ type: "objects/SET_PREFIX_WRITABLE", prefixWritable: true }
|
||||||
|
]
|
||||||
|
store.dispatch(actionsObjects.setPrefixWritable(true))
|
||||||
|
const actions = store.getActions()
|
||||||
|
expect(actions).toEqual(expectedActions)
|
||||||
|
})
|
||||||
|
|
||||||
it("creates objects/REMOVE action", () => {
|
it("creates objects/REMOVE action", () => {
|
||||||
const store = mockStore()
|
const store = mockStore()
|
||||||
const expectedActions = [{ type: "objects/REMOVE", object: "obj1" }]
|
const expectedActions = [{ type: "objects/REMOVE", object: "obj1" }]
|
||||||
|
@ -27,6 +27,7 @@ describe("objects reducer", () => {
|
|||||||
currentPrefix: "",
|
currentPrefix: "",
|
||||||
marker: "",
|
marker: "",
|
||||||
isTruncated: false,
|
isTruncated: false,
|
||||||
|
prefixWritable: false,
|
||||||
shareObject: {
|
shareObject: {
|
||||||
show: false,
|
show: false,
|
||||||
object: "",
|
object: "",
|
||||||
@ -123,6 +124,14 @@ describe("objects reducer", () => {
|
|||||||
expect(newState.isTruncated).toBeFalsy()
|
expect(newState.isTruncated).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should handle SET_PREFIX_WRITABLE", () => {
|
||||||
|
const newState = reducer(undefined, {
|
||||||
|
type: actions.SET_PREFIX_WRITABLE,
|
||||||
|
prefixWritable: true
|
||||||
|
})
|
||||||
|
expect(newState.prefixWritable).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
it("should handle SET_SHARE_OBJECT", () => {
|
it("should handle SET_SHARE_OBJECT", () => {
|
||||||
const newState = reducer(undefined, {
|
const newState = reducer(undefined, {
|
||||||
type: actions.SET_SHARE_OBJECT,
|
type: actions.SET_SHARE_OBJECT,
|
||||||
|
@ -32,6 +32,7 @@ export const REMOVE = "objects/REMOVE"
|
|||||||
export const SET_SORT_BY = "objects/SET_SORT_BY"
|
export const SET_SORT_BY = "objects/SET_SORT_BY"
|
||||||
export const SET_SORT_ORDER = "objects/SET_SORT_ORDER"
|
export const SET_SORT_ORDER = "objects/SET_SORT_ORDER"
|
||||||
export const SET_CURRENT_PREFIX = "objects/SET_CURRENT_PREFIX"
|
export const SET_CURRENT_PREFIX = "objects/SET_CURRENT_PREFIX"
|
||||||
|
export const SET_PREFIX_WRITABLE = "objects/SET_PREFIX_WRITABLE"
|
||||||
export const SET_SHARE_OBJECT = "objects/SET_SHARE_OBJECT"
|
export const SET_SHARE_OBJECT = "objects/SET_SHARE_OBJECT"
|
||||||
export const CHECKED_LIST_ADD = "objects/CHECKED_LIST_ADD"
|
export const CHECKED_LIST_ADD = "objects/CHECKED_LIST_ADD"
|
||||||
export const CHECKED_LIST_REMOVE = "objects/CHECKED_LIST_REMOVE"
|
export const CHECKED_LIST_REMOVE = "objects/CHECKED_LIST_REMOVE"
|
||||||
@ -80,6 +81,11 @@ export const fetchObjects = append => {
|
|||||||
dispatch(setSortBy(""))
|
dispatch(setSortBy(""))
|
||||||
dispatch(setSortOrder(false))
|
dispatch(setSortOrder(false))
|
||||||
}
|
}
|
||||||
|
dispatch(setPrefixWritable(res.writable))
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
dispatch(alertActions.set({ type: "danger", message: err.message }))
|
||||||
|
history.push("/login")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,6 +142,11 @@ export const setCurrentPrefix = prefix => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setPrefixWritable = prefixWritable => ({
|
||||||
|
type: SET_PREFIX_WRITABLE,
|
||||||
|
prefixWritable
|
||||||
|
})
|
||||||
|
|
||||||
export const deleteObject = object => {
|
export const deleteObject = object => {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const currentBucket = getCurrentBucket(getState())
|
const currentBucket = getCurrentBucket(getState())
|
||||||
|
@ -32,6 +32,7 @@ export default (
|
|||||||
currentPrefix: "",
|
currentPrefix: "",
|
||||||
marker: "",
|
marker: "",
|
||||||
isTruncated: false,
|
isTruncated: false,
|
||||||
|
prefixWritable: false,
|
||||||
shareObject: {
|
shareObject: {
|
||||||
show: false,
|
show: false,
|
||||||
object: "",
|
object: "",
|
||||||
@ -78,6 +79,11 @@ export default (
|
|||||||
marker: "",
|
marker: "",
|
||||||
isTruncated: false
|
isTruncated: false
|
||||||
}
|
}
|
||||||
|
case actionsObjects.SET_PREFIX_WRITABLE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
prefixWritable: action.prefixWritable
|
||||||
|
}
|
||||||
case actionsObjects.SET_SHARE_OBJECT:
|
case actionsObjects.SET_SHARE_OBJECT:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
@ -19,3 +19,5 @@ import { createSelector } from "reselect"
|
|||||||
export const getCurrentPrefix = state => state.objects.currentPrefix
|
export const getCurrentPrefix = state => state.objects.currentPrefix
|
||||||
|
|
||||||
export const getCheckedList = state => state.objects.checkedList
|
export const getCheckedList = state => state.objects.checkedList
|
||||||
|
|
||||||
|
export const getPrefixWritable = state => state.objects.prefixWritable
|
||||||
|
Loading…
x
Reference in New Issue
Block a user