mirror of
https://github.com/minio/minio.git
synced 2025-11-25 12:06:10 -05:00
deprecate embedded browser (#12163)
https://github.com/minio/console takes over the functionality for the future object browser development Signed-off-by: Harshavardhana <harsha@minio.io>
This commit is contained in:
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015-2021 MinIO, Inc.
|
||||
*
|
||||
* This file is part of MinIO Object Storage stack
|
||||
*
|
||||
* 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 from "react"
|
||||
import classNames from "classnames"
|
||||
import { connect } from "react-redux"
|
||||
import ConfirmModal from "../browser/ConfirmModal"
|
||||
import * as uploadsActions from "./actions"
|
||||
|
||||
export class AbortConfirmModal extends React.Component {
|
||||
abortUploads() {
|
||||
const { abort, uploads } = this.props
|
||||
for (var slug in uploads) {
|
||||
abort(slug)
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const { hideAbort } = this.props
|
||||
let baseClass = classNames({
|
||||
"abort-upload": true
|
||||
})
|
||||
let okIcon = classNames({
|
||||
fas: true,
|
||||
"fa-times": true
|
||||
})
|
||||
let cancelIcon = classNames({
|
||||
fas: true,
|
||||
"fa-cloud-upload-alt": true
|
||||
})
|
||||
|
||||
return (
|
||||
<ConfirmModal
|
||||
show={true}
|
||||
baseClass={baseClass}
|
||||
text="Abort uploads in progress?"
|
||||
icon="fas fa-info-circle mci-amber"
|
||||
sub="This cannot be undone!"
|
||||
okText="Abort"
|
||||
okIcon={okIcon}
|
||||
cancelText="Upload"
|
||||
cancelIcon={cancelIcon}
|
||||
okHandler={this.abortUploads.bind(this)}
|
||||
cancelHandler={hideAbort}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
uploads: state.uploads.files
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
abort: slug => dispatch(uploadsActions.abortUpload(slug)),
|
||||
hideAbort: () => dispatch(uploadsActions.hideAbortModal())
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AbortConfirmModal)
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015-2021 MinIO, Inc.
|
||||
*
|
||||
* This file is part of MinIO Object Storage stack
|
||||
*
|
||||
* 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 from "react"
|
||||
import { connect } from "react-redux"
|
||||
import ReactDropzone from "react-dropzone"
|
||||
import * as actions from "./actions"
|
||||
|
||||
// Dropzone is a drag-and-drop element for uploading files. It will create a
|
||||
// landing zone of sorts that automatically receives the files.
|
||||
export class Dropzone extends React.Component {
|
||||
onDrop(files) {
|
||||
const { uploadFile } = this.props
|
||||
// FIXME: Currently you can upload multiple files, but only one abort
|
||||
// modal will be shown, and progress updates will only occur for one
|
||||
// file at a time. See #171.
|
||||
files.forEach(file => {
|
||||
uploadFile(file)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
// Overwrite the default styling from react-dropzone; otherwise it
|
||||
// won't handle child elements correctly.
|
||||
const style = {
|
||||
flex: "1",
|
||||
borderWidth: "0",
|
||||
borderStyle: "dashed",
|
||||
borderColor: "#fff"
|
||||
}
|
||||
const activeStyle = {
|
||||
borderWidth: "2px",
|
||||
borderColor: "#777"
|
||||
}
|
||||
const rejectStyle = {
|
||||
backgroundColor: "#ffdddd"
|
||||
}
|
||||
const getStyle = (isDragActive, isDragAccept, isDragReject) => ({
|
||||
...style,
|
||||
...(isDragActive ? activeStyle : {}),
|
||||
...(isDragReject ? rejectStyle : {})
|
||||
})
|
||||
|
||||
// disableClick means that it won't trigger a file upload box when
|
||||
// the user clicks on a file.
|
||||
return (
|
||||
<ReactDropzone
|
||||
onDrop={this.onDrop.bind(this)}
|
||||
>
|
||||
{({getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject}) => (
|
||||
<div
|
||||
{...getRootProps({
|
||||
onClick: event => event.stopPropagation()
|
||||
})}
|
||||
style={getStyle(isDragActive, isDragAccept, isDragReject)}
|
||||
>
|
||||
<input {...getInputProps()} />
|
||||
{this.props.children}
|
||||
</div>
|
||||
)}
|
||||
</ReactDropzone>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
uploadFile: file => dispatch(actions.uploadFile(file))
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(undefined, mapDispatchToProps)(Dropzone)
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015-2021 MinIO, Inc.
|
||||
*
|
||||
* This file is part of MinIO Object Storage stack
|
||||
*
|
||||
* 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 from "react"
|
||||
import humanize from "humanize"
|
||||
import classNames from "classnames"
|
||||
import { connect } from "react-redux"
|
||||
|
||||
import { ProgressBar } from "react-bootstrap"
|
||||
import AbortConfirmModal from "./AbortConfirmModal"
|
||||
import * as uploadsActions from "./actions"
|
||||
|
||||
export class UploadModal extends React.Component {
|
||||
render() {
|
||||
const { uploads, showAbort, showAbortModal } = this.props
|
||||
if (showAbort) {
|
||||
return <AbortConfirmModal />
|
||||
}
|
||||
|
||||
// If we don't have any files uploading, don't show anything.
|
||||
let numberUploading = Object.keys(uploads).length
|
||||
if (numberUploading == 0) return <noscript />
|
||||
|
||||
let totalLoaded = 0
|
||||
let totalSize = 0
|
||||
|
||||
// Iterate over each upload, adding together the total size and that
|
||||
// which has been uploaded.
|
||||
for (var slug in uploads) {
|
||||
let upload = uploads[slug]
|
||||
totalLoaded += upload.loaded
|
||||
totalSize += upload.size
|
||||
}
|
||||
|
||||
let percent = totalLoaded / totalSize * 100
|
||||
|
||||
// If more than one: "Uploading files (5)..."
|
||||
// If only one: "Uploading myfile.txt..."
|
||||
let text =
|
||||
"Uploading " +
|
||||
(numberUploading == 1
|
||||
? `'${uploads[Object.keys(uploads)[0]].name}'`
|
||||
: `files (${numberUploading})`) +
|
||||
"..."
|
||||
|
||||
return (
|
||||
<div className="alert alert-info progress animated fadeInUp ">
|
||||
<button type="button" className="close" onClick={showAbortModal}>
|
||||
<span>×</span>
|
||||
</button>
|
||||
<div className="text-center">
|
||||
<small>{text}</small>
|
||||
</div>
|
||||
<ProgressBar now={percent} />
|
||||
<div className="text-center">
|
||||
<small>
|
||||
{humanize.filesize(totalLoaded)} ({percent.toFixed(2)} %)
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
uploads: state.uploads.files,
|
||||
showAbort: state.uploads.showAbortModal
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
showAbortModal: () => dispatch(uploadsActions.showAbortModal())
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UploadModal)
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015-2021 MinIO, Inc.
|
||||
*
|
||||
* This file is part of MinIO Object Storage stack
|
||||
*
|
||||
* 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 from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import { AbortConfirmModal } from "../AbortConfirmModal"
|
||||
|
||||
describe("AbortConfirmModal", () => {
|
||||
it("should render without crashing", () => {
|
||||
shallow(<AbortConfirmModal />)
|
||||
})
|
||||
|
||||
it("should call abort for every upload when Abort is clicked", () => {
|
||||
const abort = jest.fn()
|
||||
const wrapper = shallow(
|
||||
<AbortConfirmModal
|
||||
uploads={{
|
||||
"a-b/-test1": { size: 100, loaded: 50, name: "test1" },
|
||||
"a-b/-test2": { size: 100, loaded: 50, name: "test2" }
|
||||
}}
|
||||
abort={abort}
|
||||
/>
|
||||
)
|
||||
wrapper.instance().abortUploads()
|
||||
expect(abort.mock.calls.length).toBe(2)
|
||||
expect(abort.mock.calls[0][0]).toBe("a-b/-test1")
|
||||
expect(abort.mock.calls[1][0]).toBe("a-b/-test2")
|
||||
})
|
||||
|
||||
it("should call hideAbort when cancel is clicked", () => {
|
||||
const hideAbort = jest.fn()
|
||||
const wrapper = shallow(<AbortConfirmModal hideAbort={hideAbort} />)
|
||||
wrapper.find("ConfirmModal").prop("cancelHandler")()
|
||||
expect(hideAbort).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015-2021 MinIO, Inc.
|
||||
*
|
||||
* This file is part of MinIO Object Storage stack
|
||||
*
|
||||
* 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 from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import { Dropzone } from "../Dropzone"
|
||||
|
||||
describe("Dropzone", () => {
|
||||
it("should render without crashing", () => {
|
||||
shallow(<Dropzone />)
|
||||
})
|
||||
|
||||
it("should call uploadFile with files", () => {
|
||||
const uploadFile = jest.fn()
|
||||
const wrapper = shallow(<Dropzone uploadFile={uploadFile} />)
|
||||
const file1 = new Blob(["file content1"], { type: "text/plain" })
|
||||
const file2 = new Blob(["file content2"], { type: "text/plain" })
|
||||
wrapper.first().prop("onDrop")([file1, file2])
|
||||
expect(uploadFile.mock.calls).toEqual([[file1], [file2]])
|
||||
})
|
||||
})
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015-2021 MinIO, Inc.
|
||||
*
|
||||
* This file is part of MinIO Object Storage stack
|
||||
*
|
||||
* 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 from "react"
|
||||
import { shallow } from "enzyme"
|
||||
import { UploadModal } from "../UploadModal"
|
||||
|
||||
describe("UploadModal", () => {
|
||||
it("should render without crashing", () => {
|
||||
shallow(<UploadModal uploads={{}} />)
|
||||
})
|
||||
|
||||
it("should render AbortConfirmModal when showAbort is true", () => {
|
||||
const wrapper = shallow(<UploadModal uploads={{}} showAbort={true} />)
|
||||
expect(wrapper.find("Connect(AbortConfirmModal)").length).toBe(1)
|
||||
})
|
||||
|
||||
it("should render nothing when there are no files being uploaded", () => {
|
||||
const wrapper = shallow(<UploadModal uploads={{}} />)
|
||||
expect(wrapper.find("noscript").length).toBe(1)
|
||||
})
|
||||
|
||||
it("should show upload progress when one or more files are being uploaded", () => {
|
||||
const wrapper = shallow(
|
||||
<UploadModal
|
||||
uploads={{ "a-b/-test": { size: 100, loaded: 50, name: "test" } }}
|
||||
/>
|
||||
)
|
||||
expect(wrapper.find("ProgressBar").length).toBe(1)
|
||||
})
|
||||
|
||||
it("should call showAbortModal when close button is clicked", () => {
|
||||
const showAbortModal = jest.fn()
|
||||
const wrapper = shallow(
|
||||
<UploadModal
|
||||
uploads={{ "a-b/-test": { size: 100, loaded: 50, name: "test" } }}
|
||||
showAbortModal={showAbortModal}
|
||||
/>
|
||||
)
|
||||
wrapper.find("button").simulate("click")
|
||||
expect(showAbortModal).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015-2021 MinIO, Inc.
|
||||
*
|
||||
* This file is part of MinIO Object Storage stack
|
||||
*
|
||||
* 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 configureStore from "redux-mock-store"
|
||||
import thunk from "redux-thunk"
|
||||
import * as uploadsActions from "../actions"
|
||||
|
||||
const middlewares = [thunk]
|
||||
const mockStore = configureStore(middlewares)
|
||||
|
||||
describe("Uploads actions", () => {
|
||||
it("creates uploads/ADD action", () => {
|
||||
const store = mockStore()
|
||||
const expectedActions = [
|
||||
{
|
||||
type: "uploads/ADD",
|
||||
slug: "a-b-c",
|
||||
size: 100,
|
||||
name: "test"
|
||||
}
|
||||
]
|
||||
store.dispatch(uploadsActions.add("a-b-c", 100, "test"))
|
||||
const actions = store.getActions()
|
||||
expect(actions).toEqual(expectedActions)
|
||||
})
|
||||
|
||||
it("creates uploads/UPDATE_PROGRESS action", () => {
|
||||
const store = mockStore()
|
||||
const expectedActions = [
|
||||
{
|
||||
type: "uploads/UPDATE_PROGRESS",
|
||||
slug: "a-b-c",
|
||||
loaded: 50
|
||||
}
|
||||
]
|
||||
store.dispatch(uploadsActions.updateProgress("a-b-c", 50))
|
||||
const actions = store.getActions()
|
||||
expect(actions).toEqual(expectedActions)
|
||||
})
|
||||
|
||||
it("creates uploads/STOP action", () => {
|
||||
const store = mockStore()
|
||||
const expectedActions = [
|
||||
{
|
||||
type: "uploads/STOP",
|
||||
slug: "a-b-c"
|
||||
}
|
||||
]
|
||||
store.dispatch(uploadsActions.stop("a-b-c"))
|
||||
const actions = store.getActions()
|
||||
expect(actions).toEqual(expectedActions)
|
||||
})
|
||||
|
||||
it("creates uploads/SHOW_ABORT_MODAL action", () => {
|
||||
const store = mockStore()
|
||||
const expectedActions = [
|
||||
{
|
||||
type: "uploads/SHOW_ABORT_MODAL",
|
||||
show: true
|
||||
}
|
||||
]
|
||||
store.dispatch(uploadsActions.showAbortModal())
|
||||
const actions = store.getActions()
|
||||
expect(actions).toEqual(expectedActions)
|
||||
})
|
||||
|
||||
describe("uploadFile", () => {
|
||||
const file = new Blob(["file content"], {
|
||||
type: "text/plain"
|
||||
})
|
||||
file.name = "file1"
|
||||
|
||||
it("creates alerts/SET action when currentBucket is not present", () => {
|
||||
const store = mockStore({
|
||||
buckets: { currentBucket: "" }
|
||||
})
|
||||
const expectedActions = [
|
||||
{
|
||||
type: "alert/SET",
|
||||
alert: {
|
||||
id: 0,
|
||||
type: "danger",
|
||||
message: "Please choose a bucket before trying to upload files."
|
||||
}
|
||||
}
|
||||
]
|
||||
const file = new Blob(["file content"], { type: "text/plain" })
|
||||
store.dispatch(uploadsActions.uploadFile(file))
|
||||
const actions = store.getActions()
|
||||
expect(actions).toEqual(expectedActions)
|
||||
})
|
||||
|
||||
it("creates uploads/ADD action before uploading the file", () => {
|
||||
const store = mockStore({
|
||||
buckets: { currentBucket: "test1" },
|
||||
objects: { currentPrefix: "pre1/" }
|
||||
})
|
||||
const expectedActions = [
|
||||
{
|
||||
type: "uploads/ADD",
|
||||
slug: "test1-pre1/-file1",
|
||||
size: file.size,
|
||||
name: file.name
|
||||
}
|
||||
]
|
||||
store.dispatch(uploadsActions.uploadFile(file))
|
||||
const actions = store.getActions()
|
||||
expect(actions).toEqual(expectedActions)
|
||||
})
|
||||
|
||||
it("should open and send XMLHttpRequest", () => {
|
||||
const open = jest.fn()
|
||||
const send = jest.fn()
|
||||
const xhrMockClass = () => ({
|
||||
open: open,
|
||||
send: send,
|
||||
setRequestHeader: jest.fn(),
|
||||
upload: {
|
||||
addEventListener: jest.fn()
|
||||
}
|
||||
})
|
||||
window.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass)
|
||||
const store = mockStore({
|
||||
buckets: { currentBucket: "test1" },
|
||||
objects: { currentPrefix: "pre1/" }
|
||||
})
|
||||
store.dispatch(uploadsActions.uploadFile(file))
|
||||
const objectPath = encodeURIComponent("pre1/file1")
|
||||
expect(open).toHaveBeenCalledWith(
|
||||
"PUT",
|
||||
"https://localhost:8080/upload/test1/" + objectPath,
|
||||
true
|
||||
)
|
||||
expect(send).toHaveBeenCalledWith(file)
|
||||
})
|
||||
})
|
||||
|
||||
it("creates uploads/STOP and uploads/SHOW_ABORT_MODAL after abortUpload", () => {
|
||||
const store = mockStore()
|
||||
const expectedActions = [
|
||||
{
|
||||
type: "uploads/STOP",
|
||||
slug: "a-b/-c"
|
||||
},
|
||||
{
|
||||
type: "uploads/SHOW_ABORT_MODAL",
|
||||
show: false
|
||||
}
|
||||
]
|
||||
store.dispatch(uploadsActions.abortUpload("a-b/-c"))
|
||||
const actions = store.getActions()
|
||||
expect(actions).toEqual(expectedActions)
|
||||
})
|
||||
})
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015-2021 MinIO, Inc.
|
||||
*
|
||||
* This file is part of MinIO Object Storage stack
|
||||
*
|
||||
* 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 reducer from "../reducer"
|
||||
import * as actions from "../actions"
|
||||
|
||||
describe("uploads reducer", () => {
|
||||
it("should return the initial state", () => {
|
||||
const initialState = reducer(undefined, {})
|
||||
expect(initialState).toEqual({
|
||||
files: {},
|
||||
showAbortModal: false
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle ADD", () => {
|
||||
const newState = reducer(undefined, {
|
||||
type: actions.ADD,
|
||||
slug: "a-b-c",
|
||||
size: 100,
|
||||
name: "test"
|
||||
})
|
||||
expect(newState.files).toEqual({
|
||||
"a-b-c": { loaded: 0, size: 100, name: "test" }
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle UPDATE_PROGRESS", () => {
|
||||
const newState = reducer(
|
||||
{
|
||||
files: { "a-b-c": { loaded: 0, size: 100, name: "test" } }
|
||||
},
|
||||
{
|
||||
type: actions.UPDATE_PROGRESS,
|
||||
slug: "a-b-c",
|
||||
loaded: 50
|
||||
}
|
||||
)
|
||||
expect(newState.files).toEqual({
|
||||
"a-b-c": { loaded: 50, size: 100, name: "test" }
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle STOP", () => {
|
||||
const newState = reducer(
|
||||
{
|
||||
files: {
|
||||
"a-b-c": { loaded: 70, size: 100, name: "test1" },
|
||||
"x-y-z": { loaded: 50, size: 100, name: "test2" }
|
||||
}
|
||||
},
|
||||
{
|
||||
type: actions.STOP,
|
||||
slug: "a-b-c"
|
||||
}
|
||||
)
|
||||
expect(newState.files).toEqual({
|
||||
"x-y-z": { loaded: 50, size: 100, name: "test2" }
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle SHOW_ABORT_MODAL", () => {
|
||||
const newState = reducer(
|
||||
{
|
||||
showAbortModal: false
|
||||
},
|
||||
{
|
||||
type: actions.SHOW_ABORT_MODAL,
|
||||
show: true
|
||||
}
|
||||
)
|
||||
expect(newState.showAbortModal).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015-2021 MinIO, Inc.
|
||||
*
|
||||
* This file is part of MinIO Object Storage stack
|
||||
*
|
||||
* 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 Moment from "moment"
|
||||
import storage from "local-storage-fallback"
|
||||
import * as alertActions from "../alert/actions"
|
||||
import * as objectsActions from "../objects/actions"
|
||||
import { getCurrentBucket } from "../buckets/selectors"
|
||||
import { getCurrentPrefix } from "../objects/selectors"
|
||||
import { minioBrowserPrefix } from "../constants"
|
||||
|
||||
export const ADD = "uploads/ADD"
|
||||
export const UPDATE_PROGRESS = "uploads/UPDATE_PROGRESS"
|
||||
export const STOP = "uploads/STOP"
|
||||
export const SHOW_ABORT_MODAL = "uploads/SHOW_ABORT_MODAL"
|
||||
|
||||
export const add = (slug, size, name) => ({
|
||||
type: ADD,
|
||||
slug,
|
||||
size,
|
||||
name
|
||||
})
|
||||
|
||||
export const updateProgress = (slug, loaded) => ({
|
||||
type: UPDATE_PROGRESS,
|
||||
slug,
|
||||
loaded
|
||||
})
|
||||
|
||||
export const stop = slug => ({
|
||||
type: STOP,
|
||||
slug
|
||||
})
|
||||
|
||||
export const showAbortModal = () => ({
|
||||
type: SHOW_ABORT_MODAL,
|
||||
show: true
|
||||
})
|
||||
|
||||
export const hideAbortModal = () => ({
|
||||
type: SHOW_ABORT_MODAL,
|
||||
show: false
|
||||
})
|
||||
|
||||
let requests = {}
|
||||
|
||||
export const addUpload = (xhr, slug, size, name) => {
|
||||
return function(dispatch) {
|
||||
requests[slug] = xhr
|
||||
dispatch(add(slug, size, name))
|
||||
}
|
||||
}
|
||||
|
||||
export const abortUpload = slug => {
|
||||
return function(dispatch) {
|
||||
const xhr = requests[slug]
|
||||
if (xhr) {
|
||||
xhr.abort()
|
||||
}
|
||||
dispatch(stop(slug))
|
||||
dispatch(hideAbortModal())
|
||||
}
|
||||
}
|
||||
|
||||
export const uploadFile = file => {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState()
|
||||
const currentBucket = getCurrentBucket(state)
|
||||
if (!currentBucket) {
|
||||
dispatch(
|
||||
alertActions.set({
|
||||
type: "danger",
|
||||
message: "Please choose a bucket before trying to upload files."
|
||||
})
|
||||
)
|
||||
return
|
||||
}
|
||||
const currentPrefix = getCurrentPrefix(state)
|
||||
var _filePath = file.path || file.name
|
||||
if (_filePath.charAt(0) == '/') {
|
||||
_filePath = _filePath.substring(1)
|
||||
}
|
||||
const filePath = _filePath
|
||||
const objectName = encodeURIComponent(`${currentPrefix}${filePath}`)
|
||||
const uploadUrl = `${
|
||||
window.location.origin
|
||||
}${minioBrowserPrefix}/upload/${currentBucket}/${objectName}`
|
||||
const slug = `${currentBucket}-${currentPrefix}-${filePath}`
|
||||
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open("PUT", uploadUrl, true)
|
||||
xhr.withCredentials = false
|
||||
const token = storage.getItem("token")
|
||||
if (token) {
|
||||
xhr.setRequestHeader(
|
||||
"Authorization",
|
||||
"Bearer " + storage.getItem("token")
|
||||
)
|
||||
}
|
||||
xhr.setRequestHeader(
|
||||
"x-amz-date",
|
||||
Moment()
|
||||
.utc()
|
||||
.format("YYYYMMDDTHHmmss") + "Z"
|
||||
)
|
||||
|
||||
dispatch(addUpload(xhr, slug, file.size, file.name))
|
||||
|
||||
xhr.onload = function(event) {
|
||||
if (xhr.status == 401 || xhr.status == 403) {
|
||||
dispatch(hideAbortModal())
|
||||
dispatch(stop(slug))
|
||||
dispatch(
|
||||
alertActions.set({
|
||||
type: "danger",
|
||||
message: "Unauthorized request."
|
||||
})
|
||||
)
|
||||
}
|
||||
if (xhr.status == 500) {
|
||||
dispatch(hideAbortModal())
|
||||
dispatch(stop(slug))
|
||||
dispatch(
|
||||
alertActions.set({
|
||||
type: "danger",
|
||||
message: xhr.responseText
|
||||
})
|
||||
)
|
||||
}
|
||||
if (xhr.status == 200) {
|
||||
dispatch(hideAbortModal())
|
||||
dispatch(stop(slug))
|
||||
dispatch(
|
||||
alertActions.set({
|
||||
type: "success",
|
||||
message: "File '" + filePath + "' uploaded successfully."
|
||||
})
|
||||
)
|
||||
dispatch(objectsActions.selectPrefix(currentPrefix))
|
||||
}
|
||||
}
|
||||
|
||||
xhr.upload.addEventListener("error", event => {
|
||||
dispatch(stop(slug))
|
||||
dispatch(
|
||||
alertActions.set({
|
||||
type: "danger",
|
||||
message: "Error occurred uploading '" + filePath + "'."
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
xhr.upload.addEventListener("progress", event => {
|
||||
if (event.lengthComputable) {
|
||||
let loaded = event.loaded
|
||||
let total = event.total
|
||||
// Update the counter
|
||||
dispatch(updateProgress(slug, loaded))
|
||||
}
|
||||
})
|
||||
|
||||
xhr.send(file)
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015-2021 MinIO, Inc.
|
||||
*
|
||||
* This file is part of MinIO Object Storage stack
|
||||
*
|
||||
* 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 * as uploadsActions from "./actions"
|
||||
|
||||
const add = (files, action) => ({
|
||||
...files,
|
||||
[action.slug]: {
|
||||
loaded: 0,
|
||||
size: action.size,
|
||||
name: action.name
|
||||
}
|
||||
})
|
||||
|
||||
const updateProgress = (files, action) => ({
|
||||
...files,
|
||||
[action.slug]: {
|
||||
...files[action.slug],
|
||||
loaded: action.loaded
|
||||
}
|
||||
})
|
||||
|
||||
const stop = (files, action) => {
|
||||
const newFiles = Object.assign({}, files)
|
||||
delete newFiles[action.slug]
|
||||
return newFiles
|
||||
}
|
||||
|
||||
export default (state = { files: {}, showAbortModal: false }, action) => {
|
||||
switch (action.type) {
|
||||
case uploadsActions.ADD:
|
||||
return {
|
||||
...state,
|
||||
files: add(state.files, action)
|
||||
}
|
||||
case uploadsActions.UPDATE_PROGRESS:
|
||||
return {
|
||||
...state,
|
||||
files: updateProgress(state.files, action)
|
||||
}
|
||||
case uploadsActions.STOP:
|
||||
return {
|
||||
...state,
|
||||
files: stop(state.files, action)
|
||||
}
|
||||
case uploadsActions.SHOW_ABORT_MODAL:
|
||||
return {
|
||||
...state,
|
||||
showAbortModal: action.show
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user