Refactor bucket delete and bucket policy (#5580)
This commit adds the bucket delete and bucket policy functionalities to the browser. Part of rewriting the browser code to follow best practices and guidelines of React (issues #5409 and #5410) The backend code has been modified by @krishnasrinivas to prevent issue #4498 from occuring. The relevant changes have been made to the code according to the latest commit and the unit tests in the backend. This commit also addresses issue #5449.master
parent
416841869a
commit
a6adef0bdf
@ -0,0 +1,92 @@ |
||||
/* |
||||
* Minio Cloud Storage (C) 2018 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import React from "react" |
||||
import classNames from "classnames" |
||||
import { connect } from "react-redux" |
||||
import * as actionsBuckets from "./actions" |
||||
import { getCurrentBucket } from "./selectors" |
||||
import Dropdown from "react-bootstrap/lib/Dropdown" |
||||
|
||||
export class BucketDropdown extends React.Component { |
||||
constructor(props) { |
||||
super(props) |
||||
this.state = { |
||||
showBucketDropdown: false |
||||
} |
||||
} |
||||
|
||||
toggleDropdown() { |
||||
if (this.state.showBucketDropdown) { |
||||
this.setState({ |
||||
showBucketDropdown: false |
||||
}) |
||||
} else { |
||||
this.setState({ |
||||
showBucketDropdown: true |
||||
}) |
||||
} |
||||
} |
||||
|
||||
render() { |
||||
const { bucket, showBucketPolicy, deleteBucket, currentBucket } = this.props |
||||
return ( |
||||
<Dropdown
|
||||
open = {this.state.showBucketDropdown} |
||||
onToggle = {this.toggleDropdown.bind(this)} |
||||
className="bucket-dropdown"
|
||||
id="bucket-dropdown" |
||||
> |
||||
<Dropdown.Toggle noCaret> |
||||
<i className="zmdi zmdi-more-vert" /> |
||||
</Dropdown.Toggle> |
||||
<Dropdown.Menu className="dropdown-menu-right"> |
||||
<li> |
||||
<a
|
||||
onClick={e => { |
||||
e.stopPropagation() |
||||
this.toggleDropdown() |
||||
showBucketPolicy() |
||||
}} |
||||
> |
||||
Edit policy |
||||
</a> |
||||
</li> |
||||
<li> |
||||
<a
|
||||
onClick={e => { |
||||
e.stopPropagation() |
||||
this.toggleDropdown() |
||||
deleteBucket(bucket) |
||||
}} |
||||
> |
||||
Delete |
||||
</a> |
||||
</li> |
||||
</Dropdown.Menu> |
||||
</Dropdown> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const mapDispatchToProps = dispatch => { |
||||
return { |
||||
deleteBucket: bucket => dispatch(actionsBuckets.deleteBucket(bucket)), |
||||
showBucketPolicy: () => dispatch(actionsBuckets.showBucketPolicy()) |
||||
} |
||||
} |
||||
|
||||
export default connect(state => state, mapDispatchToProps)(BucketDropdown) |
@ -0,0 +1,61 @@ |
||||
/* |
||||
* Minio Cloud Storage (C) 2018 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import React from "react" |
||||
import { connect } from "react-redux" |
||||
import { Modal, ModalHeader } from "react-bootstrap" |
||||
import * as actionsBuckets from "./actions" |
||||
import PolicyInput from "./PolicyInput" |
||||
import Policy from "./Policy" |
||||
|
||||
export const BucketPolicyModal = ({ showBucketPolicy, currentBucket, hideBucketPolicy, policies }) => { |
||||
return ( |
||||
<Modal className="modal-policy" |
||||
animation={ false } |
||||
show={ showBucketPolicy } |
||||
onHide={ hideBucketPolicy } |
||||
> |
||||
<ModalHeader> |
||||
Bucket Policy ( |
||||
{ currentBucket }) |
||||
<button className="close close-alt" onClick={ hideBucketPolicy }> |
||||
<span>×</span> |
||||
</button> |
||||
</ModalHeader> |
||||
<div className="pm-body"> |
||||
<PolicyInput /> |
||||
{ policies.map((policy, i) => <Policy key={ i } prefix={ policy.prefix } policy={ policy.policy } /> |
||||
) } |
||||
</div> |
||||
</Modal> |
||||
) |
||||
} |
||||
|
||||
const mapStateToProps = state => { |
||||
return { |
||||
currentBucket: state.buckets.currentBucket, |
||||
showBucketPolicy: state.buckets.showBucketPolicy, |
||||
policies: state.buckets.policies |
||||
} |
||||
} |
||||
|
||||
const mapDispatchToProps = dispatch => { |
||||
return { |
||||
hideBucketPolicy: () => dispatch(actionsBuckets.hideBucketPolicy()) |
||||
} |
||||
} |
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(BucketPolicyModal) |
@ -0,0 +1,93 @@ |
||||
/* |
||||
* Minio Cloud Storage (C) 2018 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import { READ_ONLY, WRITE_ONLY, READ_WRITE } from '../constants' |
||||
|
||||
import React from "react" |
||||
import { connect } from "react-redux" |
||||
import classnames from "classnames" |
||||
import * as actionsBuckets from "./actions" |
||||
import * as actionsAlert from "../alert/actions" |
||||
import web from "../web" |
||||
|
||||
export class Policy extends React.Component { |
||||
removePolicy(e) { |
||||
e.preventDefault() |
||||
const {currentBucket, prefix, fetchPolicies, showAlert} = this.props |
||||
web. |
||||
SetBucketPolicy({ |
||||
bucketName: currentBucket, |
||||
prefix: prefix, |
||||
policy: 'none' |
||||
}) |
||||
.then(() => { |
||||
fetchPolicies(currentBucket) |
||||
}) |
||||
.catch(e => showAlert('danger', e.message)) |
||||
} |
||||
|
||||
render() { |
||||
const {policy, prefix} = this.props |
||||
let newPrefix = prefix |
||||
|
||||
if (newPrefix === '') |
||||
newPrefix = '*' |
||||
|
||||
return ( |
||||
<div className="pmb-list"> |
||||
<div className="pmbl-item"> |
||||
{ newPrefix } |
||||
</div> |
||||
<div className="pmbl-item"> |
||||
<select className="form-control" |
||||
disabled |
||||
value={ policy }> |
||||
<option value={ READ_ONLY }> |
||||
Read Only |
||||
</option> |
||||
<option value={ WRITE_ONLY }> |
||||
Write Only |
||||
</option> |
||||
<option value={ READ_WRITE }> |
||||
Read and Write |
||||
</option> |
||||
</select> |
||||
</div> |
||||
<div className="pmbl-item"> |
||||
<button className="btn btn-block btn-danger" onClick={ this.removePolicy.bind(this) }> |
||||
Remove |
||||
</button> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const mapStateToProps = state => { |
||||
return { |
||||
currentBucket: state.buckets.currentBucket |
||||
} |
||||
} |
||||
|
||||
const mapDispatchToProps = dispatch => { |
||||
return { |
||||
fetchPolicies: bucket => dispatch(actionsBuckets.fetchPolicies(bucket)), |
||||
showAlert: (type, message) => |
||||
dispatch(actionsAlert.set({ type: type, message: message })) |
||||
} |
||||
} |
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Policy) |
@ -0,0 +1,115 @@ |
||||
/* |
||||
* Minio Cloud Storage (C) 2018 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import { READ_ONLY, WRITE_ONLY, READ_WRITE } from '../constants' |
||||
|
||||
import React from "react" |
||||
import { connect } from "react-redux" |
||||
import classnames from "classnames" |
||||
import * as actionsBuckets from "./actions" |
||||
import * as actionsAlert from "../alert/actions" |
||||
import web from "../web" |
||||
|
||||
export class PolicyInput extends React.Component { |
||||
componentDidMount() { |
||||
const { currentBucket, fetchPolicies } = this.props |
||||
fetchPolicies(currentBucket) |
||||
} |
||||
|
||||
componentWillUnmount() { |
||||
const { setPolicies } = this.props |
||||
setPolicies([]) |
||||
} |
||||
|
||||
handlePolicySubmit(e) { |
||||
e.preventDefault() |
||||
const { currentBucket, fetchPolicies, showAlert } = this.props |
||||
|
||||
if (this.prefix.value === "*") |
||||
this.prefix.value = "" |
||||
|
||||
let policyAlreadyExists = this.props.policies.some( |
||||
elem => this.prefix.value === elem.prefix && this.policy.value === elem.policy |
||||
) |
||||
if (policyAlreadyExists) { |
||||
showAlert("danger", "Policy for this prefix already exists.") |
||||
return |
||||
} |
||||
|
||||
web. |
||||
SetBucketPolicy({ |
||||
bucketName: currentBucket, |
||||
prefix: this.prefix.value, |
||||
policy: this.policy.value |
||||
}) |
||||
.then(() => { |
||||
fetchPolicies(currentBucket) |
||||
this.prefix.value = '' |
||||
}) |
||||
.catch(e => showAlert("danger", e.message)) |
||||
} |
||||
|
||||
render() { |
||||
return ( |
||||
<header className="pmb-list"> |
||||
<div className="pmbl-item"> |
||||
<input
|
||||
type="text" |
||||
ref={ prefix => this.prefix = prefix } |
||||
className="form-control" |
||||
placeholder="Prefix" |
||||
/> |
||||
</div> |
||||
<div className="pmbl-item"> |
||||
<select ref={ policy => this.policy = policy } className="form-control"> |
||||
<option value={ READ_ONLY }> |
||||
Read Only |
||||
</option> |
||||
<option value={ WRITE_ONLY }> |
||||
Write Only |
||||
</option> |
||||
<option value={ READ_WRITE }> |
||||
Read and Write |
||||
</option> |
||||
</select> |
||||
</div> |
||||
<div className="pmbl-item"> |
||||
<button className="btn btn-block btn-primary" onClick={ this.handlePolicySubmit.bind(this) }> |
||||
Add |
||||
</button> |
||||
</div> |
||||
</header> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const mapStateToProps = state => { |
||||
return { |
||||
currentBucket: state.buckets.currentBucket, |
||||
policies: state.buckets.policies |
||||
} |
||||
} |
||||
|
||||
const mapDispatchToProps = dispatch => { |
||||
return { |
||||
fetchPolicies: bucket => dispatch(actionsBuckets.fetchPolicies(bucket)), |
||||
setPolicies: policies => dispatch(actionsBuckets.setPolicies(policies)), |
||||
showAlert: (type, message) => |
||||
dispatch(actionsAlert.set({ type: type, message: message })) |
||||
} |
||||
} |
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(PolicyInput) |
@ -0,0 +1,62 @@ |
||||
/* |
||||
* Minio Cloud Storage (C) 2018 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import React from "react" |
||||
import { shallow, mount } from "enzyme" |
||||
import { BucketDropdown } from "../BucketDropdown" |
||||
|
||||
describe("BucketDropdown", () => { |
||||
it("should render without crashing", () => { |
||||
shallow(<BucketDropdown />) |
||||
}) |
||||
|
||||
it("should call toggleDropdown on dropdown toggle", () => { |
||||
const spy = jest.spyOn(BucketDropdown.prototype, 'toggleDropdown') |
||||
const wrapper = shallow( |
||||
<BucketDropdown /> |
||||
) |
||||
wrapper |
||||
.find("Uncontrolled(Dropdown)") |
||||
.simulate("toggle") |
||||
expect(spy).toHaveBeenCalled() |
||||
spy.mockReset() |
||||
spy.mockRestore() |
||||
}) |
||||
|
||||
it("should call showBucketPolicy when Edit Policy link is clicked", () => { |
||||
const showBucketPolicy = jest.fn() |
||||
const wrapper = shallow( |
||||
<BucketDropdown showBucketPolicy={showBucketPolicy} /> |
||||
) |
||||
wrapper |
||||
.find("li a") |
||||
.at(0) |
||||
.simulate("click", { stopPropagation: jest.fn() }) |
||||
expect(showBucketPolicy).toHaveBeenCalled() |
||||
}) |
||||
|
||||
it("should call deleteBucket when Delete link is clicked", () => { |
||||
const deleteBucket = jest.fn() |
||||
const wrapper = shallow( |
||||
<BucketDropdown bucket={"test"} deleteBucket={deleteBucket} /> |
||||
) |
||||
wrapper |
||||
.find("li a") |
||||
.at(1) |
||||
.simulate("click", { stopPropagation: jest.fn() }) |
||||
expect(deleteBucket).toHaveBeenCalledWith("test") |
||||
}) |
||||
}) |
@ -0,0 +1,43 @@ |
||||
/* |
||||
* Minio Cloud Storage (C) 2018 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import React from "react" |
||||
import { shallow, mount } from "enzyme" |
||||
import { BucketPolicyModal } from "../BucketPolicyModal" |
||||
import { READ_ONLY, WRITE_ONLY, READ_WRITE } from "../../constants" |
||||
|
||||
describe("BucketPolicyModal", () => { |
||||
it("should render without crashing", () => { |
||||
shallow(<BucketPolicyModal policies={[]}/>) |
||||
}) |
||||
|
||||
it("should call hideBucketPolicy when close button is clicked", () => { |
||||
const hideBucketPolicy = jest.fn() |
||||
const wrapper = shallow( |
||||
<BucketPolicyModal hideBucketPolicy={hideBucketPolicy} policies={[]} /> |
||||
) |
||||
wrapper.find("button").simulate("click") |
||||
expect(hideBucketPolicy).toHaveBeenCalled() |
||||
}) |
||||
|
||||
it("should include the PolicyInput and Policy components when there are any policies", () => { |
||||
const wrapper = shallow( |
||||
<BucketPolicyModal policies={ [{prefix: "test", policy: READ_ONLY}] } /> |
||||
) |
||||
expect(wrapper.find("Connect(PolicyInput)").length).toBe(1) |
||||
expect(wrapper.find("Connect(Policy)").length).toBe(1) |
||||
}) |
||||
}) |
@ -0,0 +1,63 @@ |
||||
/* |
||||
* Minio Cloud Storage (C) 2018 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import React from "react" |
||||
import { shallow, mount } from "enzyme" |
||||
import { Policy } from "../Policy" |
||||
import { READ_ONLY, WRITE_ONLY, READ_WRITE } from "../../constants" |
||||
import web from "../../web" |
||||
|
||||
jest.mock("../../web", () => ({ |
||||
SetBucketPolicy: jest.fn(() => { |
||||
return Promise.resolve() |
||||
}) |
||||
})) |
||||
|
||||
describe("Policy", () => { |
||||
it("should render without crashing", () => { |
||||
shallow(<Policy currentBucket={"bucket"} prefix={"foo"} policy={READ_ONLY} />) |
||||
}) |
||||
|
||||
it("should call web.setBucketPolicy and fetchPolicies on submit", () => { |
||||
const fetchPolicies = jest.fn() |
||||
const wrapper = shallow( |
||||
<Policy
|
||||
currentBucket={"bucket"} |
||||
prefix={"foo"} |
||||
policy={READ_ONLY} |
||||
fetchPolicies={fetchPolicies} |
||||
/> |
||||
) |
||||
wrapper.find("button").simulate("click", { preventDefault: jest.fn() }) |
||||
|
||||
expect(web.SetBucketPolicy).toHaveBeenCalledWith({ |
||||
bucketName: "bucket", |
||||
prefix: "foo", |
||||
policy: "none" |
||||
}) |
||||
|
||||
setImmediate(() => { |
||||
expect(fetchPolicies).toHaveBeenCalledWith("bucket") |
||||
}) |
||||
}) |
||||
|
||||
it("should change the empty string to '*' while displaying prefixes", () => { |
||||
const wrapper = shallow( |
||||
<Policy currentBucket={"bucket"} prefix={""} policy={READ_ONLY} /> |
||||
) |
||||
expect(wrapper.find(".pmbl-item").at(0).text()).toEqual("*") |
||||
}) |
||||
}) |
@ -0,0 +1,77 @@ |
||||
/* |
||||
* Minio Cloud Storage (C) 2018 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import React from "react" |
||||
import { shallow, mount } from "enzyme" |
||||
import { PolicyInput } from "../PolicyInput" |
||||
import { READ_ONLY, WRITE_ONLY, READ_WRITE } from "../../constants" |
||||
import web from "../../web" |
||||
|
||||
jest.mock("../../web", () => ({ |
||||
SetBucketPolicy: jest.fn(() => { |
||||
return Promise.resolve() |
||||
}) |
||||
})) |
||||
|
||||
describe("PolicyInput", () => { |
||||
it("should render without crashing", () => { |
||||
const fetchPolicies = jest.fn() |
||||
shallow(<PolicyInput currentBucket={"bucket"} fetchPolicies={fetchPolicies}/>) |
||||
}) |
||||
|
||||
it("should call fetchPolicies after the component has mounted", () => { |
||||
const fetchPolicies = jest.fn() |
||||
const wrapper = shallow( |
||||
<PolicyInput currentBucket={"bucket"} fetchPolicies={fetchPolicies} /> |
||||
) |
||||
setImmediate(() => { |
||||
expect(fetchPolicies).toHaveBeenCalled() |
||||
}) |
||||
}) |
||||
|
||||
it("should call web.setBucketPolicy and fetchPolicies on submit", () => { |
||||
const fetchPolicies = jest.fn() |
||||
const wrapper = shallow( |
||||
<PolicyInput currentBucket={"bucket"} policies={[]} fetchPolicies={fetchPolicies}/> |
||||
) |
||||
wrapper.instance().prefix = { value: "baz" } |
||||
wrapper.instance().policy = { value: READ_ONLY } |
||||
wrapper.find("button").simulate("click", { preventDefault: jest.fn() }) |
||||
|
||||
expect(web.SetBucketPolicy).toHaveBeenCalledWith({ |
||||
bucketName: "bucket", |
||||
prefix: "baz", |
||||
policy: READ_ONLY |
||||
}) |
||||
|
||||
setImmediate(() => { |
||||
expect(fetchPolicies).toHaveBeenCalledWith("bucket") |
||||
}) |
||||
}) |
||||
|
||||
it("should change the prefix '*' to an empty string", () => { |
||||
const fetchPolicies = jest.fn() |
||||
const wrapper = shallow( |
||||
<PolicyInput currentBucket={"bucket"} policies={[]} fetchPolicies={fetchPolicies}/> |
||||
) |
||||
wrapper.instance().prefix = { value: "*" } |
||||
wrapper.instance().policy = { value: READ_ONLY } |
||||
|
||||
wrapper.find("button").simulate("click", { preventDefault: jest.fn() }) |
||||
|
||||
expect(wrapper.instance().prefix).toEqual({ value: "" }) |
||||
}) |
||||
}) |
@ -1,80 +0,0 @@ |
||||
import { READ_ONLY, WRITE_ONLY, READ_WRITE } from '../constants' |
||||
|
||||
import React, { Component, PropTypes } from 'react' |
||||
import connect from 'react-redux/lib/components/connect' |
||||
import classnames from 'classnames' |
||||
import * as actions from '../actions' |
||||
|
||||
class Policy extends Component { |
||||
constructor(props, context) { |
||||
super(props, context) |
||||
this.state = {} |
||||
} |
||||
|
||||
handlePolicyChange(e) { |
||||
this.setState({ |
||||
policy: { |
||||
policy: e.target.value |
||||
} |
||||
}) |
||||
} |
||||
|
||||
removePolicy(e) { |
||||
e.preventDefault() |
||||
const {dispatch, currentBucket, prefix} = this.props |
||||
let newPrefix = prefix.replace(currentBucket + '/', '') |
||||
newPrefix = newPrefix.replace('*', '') |
||||
web.SetBucketPolicy({ |
||||
bucketName: currentBucket, |
||||
prefix: newPrefix, |
||||
policy: 'none' |
||||
}) |
||||
.then(() => { |
||||
dispatch(actions.setPolicies(this.props.policies.filter(policy => policy.prefix != prefix))) |
||||
}) |
||||
.catch(e => dispatch(actions.showAlert({ |
||||
type: 'danger', |
||||
message: e.message, |
||||
}))) |
||||
} |
||||
|
||||
render() { |
||||
const {policy, prefix, currentBucket} = this.props |
||||
let newPrefix = prefix.replace(currentBucket + '/', '') |
||||
newPrefix = newPrefix.replace('*', '') |
||||
|
||||
if (!newPrefix) |
||||
newPrefix = '*' |
||||
|
||||
return ( |
||||
<div className="pmb-list"> |
||||
<div className="pmbl-item"> |
||||
{ newPrefix } |
||||
</div> |
||||
<div className="pmbl-item"> |
||||
<select className="form-control" |
||||
disabled |
||||
value={ policy } |
||||
onChange={ this.handlePolicyChange.bind(this) }> |
||||
<option value={ READ_ONLY }> |
||||
Read Only |
||||
</option> |
||||
<option value={ WRITE_ONLY }> |
||||
Write Only |
||||
</option> |
||||
<option value={ READ_WRITE }> |
||||
Read and Write |
||||
</option> |
||||
</select> |
||||
</div> |
||||
<div className="pmbl-item"> |
||||
<button className="btn btn-block btn-danger" onClick={ this.removePolicy.bind(this) }> |
||||
Remove |
||||
</button> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
||||
} |
||||
|
||||
export default connect(state => state)(Policy) |
@ -1,98 +0,0 @@ |
||||
import { READ_ONLY, WRITE_ONLY, READ_WRITE } from '../constants' |
||||
import React, { Component, PropTypes } from 'react' |
||||
import connect from 'react-redux/lib/components/connect' |
||||
import classnames from 'classnames' |
||||
import * as actions from '../actions' |
||||
|
||||
class PolicyInput extends Component { |
||||
componentDidMount() { |
||||
const {web, dispatch} = this.props |
||||
this.prefix.focus() |
||||
web.ListAllBucketPolicies({ |
||||
bucketName: this.props.currentBucket |
||||
}).then(res => { |
||||
let policies = res.policies |
||||
if (policies) dispatch(actions.setPolicies(policies)) |
||||
}).catch(err => { |
||||
dispatch(actions.showAlert({ |
||||
type: 'danger', |
||||
message: err.message |
||||
})) |
||||
}) |
||||
} |
||||
|
||||
componentWillUnmount() { |
||||
const {dispatch} = this.props |
||||
dispatch(actions.setPolicies([])) |
||||
} |
||||
|
||||
handlePolicySubmit(e) { |
||||
e.preventDefault() |
||||
const {web, dispatch, currentBucket} = this.props |
||||
|
||||
let prefix = currentBucket + '/' + this.prefix.value |
||||
let policy = this.policy.value |
||||
|
||||
if (!prefix.endsWith('*')) prefix = prefix + '*' |
||||
|
||||
let prefixAlreadyExists = this.props.policies.some(elem => prefix === elem.prefix) |
||||
|
||||
if (prefixAlreadyExists) { |
||||
dispatch(actions.showAlert({ |
||||
type: 'danger', |
||||
message: "Policy for this prefix already exists." |
||||
})) |
||||
return |
||||
} |
||||
|
||||
web.SetBucketPolicy({ |
||||
bucketName: this.props.currentBucket, |
||||
prefix: this.prefix.value, |
||||
policy: this.policy.value |
||||
}) |
||||
.then(() => { |
||||
dispatch(actions.setPolicies([{ |
||||
policy, prefix |
||||
}, ...this.props.policies])) |
||||
this.prefix.value = '' |
||||
}) |
||||
.catch(e => dispatch(actions.showAlert({ |
||||
type: 'danger', |
||||
message: e.message, |
||||
}))) |
||||
} |
||||
|
||||
render() { |
||||
return ( |
||||
<header className="pmb-list"> |
||||
<div className="pmbl-item"> |
||||
<input type="text" |
||||
ref={ prefix => this.prefix = prefix } |
||||
className="form-control" |
||||
placeholder="Prefix" |
||||
editable={ true } /> |
||||
</div> |
||||
<div className="pmbl-item"> |
||||
<select ref={ policy => this.policy = policy } className="form-control"> |
||||
<option value={ READ_ONLY }> |
||||
Read Only |
||||
</option> |
||||
<option value={ WRITE_ONLY }> |
||||
Write Only |
||||
</option> |
||||
<option value={ READ_WRITE }> |
||||
Read and Write |
||||
</option> |
||||
</select> |
||||
</div> |
||||
<div className="pmbl-item"> |
||||
<button className="btn btn-block btn-primary" onClick={ this.handlePolicySubmit.bind(this) }> |
||||
Add |
||||
</button> |
||||
</div> |
||||
</header> |
||||
) |
||||
} |
||||
} |
||||
|
||||
export default connect(state => state)(PolicyInput) |
Loading…
Reference in new issue