Просмотр исходного кода

Add 'Delete Replica Disks' button to Replica page

Immediately add the 'Delete' response to the current executions and
redirect to 'Replica Executions' page.
Disable the `Delete` button if replica doesn't have any executions.
Sergiu Miclea 8 лет назад
Родитель
Сommit
7434a71b98

+ 16 - 0
src/actions/ReplicaActions.js

@@ -177,6 +177,22 @@ class ReplicaActions {
   clearDetails() {
     return true
   }
+
+  deleteDisks(replicaId) {
+    ReplicaSource.deleteDisks(replicaId).then(
+      execution => { this.deleteDisksSuccess(replicaId, execution) },
+      response => { this.deleteDisksFailed(response) },
+    )
+    return replicaId
+  }
+
+  deleteDisksSuccess(replicaId, execution) {
+    return { replicaId, execution }
+  }
+
+  deleteDisksFailed(execution) {
+    return execution || true
+  }
 }
 
 export default alt.createActions(ReplicaActions)

+ 34 - 12
src/components/organisms/ReplicaDetailsContent/ReplicaDetailsContent.jsx

@@ -24,8 +24,18 @@ const Wrapper = styled.div`
 `
 
 const Buttons = styled.div`
-  & > button:last-child {
-    float: right;
+  display: flex;
+  justify-content: space-between;
+`
+const LeftButtons = styled.div``
+const RightButtons = styled.div`
+  display: flex;
+  button {
+    margin-right: 16px;
+
+    &:last-child {
+      margin-right: 0;
+    }
   }
 `
 const DetailsBody = styled.div`
@@ -58,6 +68,7 @@ class ReplicaDetailsContent extends React.Component {
     onExecuteClick: PropTypes.func,
     onCreateMigrationClick: PropTypes.func,
     onDeleteReplicaClick: PropTypes.func,
+    onDeleteReplicaDisksClick: PropTypes.func,
     onAddScheduleClick: PropTypes.func,
     onScheduleChange: PropTypes.func,
     onScheduleRemove: PropTypes.func,
@@ -95,16 +106,27 @@ class ReplicaDetailsContent extends React.Component {
   renderBottomControls() {
     return (
       <Buttons>
-        <Button
-          primary
-          disabled={this.getStatus() !== 'COMPLETED' || this.isEndpointMissing()}
-          onClick={this.props.onCreateMigrationClick}
-        >Create Migration</Button>
-        <Button
-          alert
-          hollow
-          onClick={this.props.onDeleteReplicaClick}
-        >Delete Replica</Button>
+        <LeftButtons>
+          <Button
+            primary
+            disabled={this.getStatus() !== 'COMPLETED' || this.isEndpointMissing()}
+            onClick={this.props.onCreateMigrationClick}
+          >Create Migration</Button>
+        </LeftButtons>
+        <RightButtons>
+          <Button
+            alert
+            hollow
+            secondary
+            onClick={this.props.onDeleteReplicaDisksClick}
+            disabled={!this.props.item.executions || this.props.item.executions.length === 0}
+          >Delete Replica Disks</Button>
+          <Button
+            alert
+            hollow
+            onClick={this.props.onDeleteReplicaClick}
+          >Delete Replica</Button>
+        </RightButtons>
       </Buttons>
     )
   }

+ 5 - 5
src/components/organisms/ReplicaDetailsContent/test.jsx

@@ -60,7 +60,7 @@ it('renders schedule page', () => {
 
 it('has `Create migration` button disabled if the last status is not completed', () => {
   let wrapper = wrap({ endpoints, item, page: '' })
-  expect((wrapper.find('MainDetails').prop('bottomControls').props.children[0].props.disabled)).toBe(true)
+  expect(wrapper.find('MainDetails').prop('bottomControls').props.children[0].props.children.props.disabled).toBe(true)
 })
 
 it('has `Create migration` button enabled if the last status is completed', () => {
@@ -69,13 +69,13 @@ it('has `Create migration` button enabled if the last status is completed', () =
     executions: [...item.executions, { id: 'execution-4', status: 'COMPLETED', created_at: new Date() }],
   }
   let wrapper = wrap({ endpoints, item: newItem, page: '' })
-  expect((wrapper.find('MainDetails').prop('bottomControls').props.children[0].props.disabled)).toBe(false)
+  expect(wrapper.find('MainDetails').prop('bottomControls').props.children[0].props.children.props.disabled).toBe(false)
 })
 
 it('dispaches create migration click', () => {
   let onCreateMigrationClick = sinon.spy()
   let wrapper = wrap({ endpoints, item, page: '', onCreateMigrationClick })
-  wrapper.find('MainDetails').prop('bottomControls').props.children[0].props.onClick()
+  wrapper.find('MainDetails').prop('bottomControls').props.children[0].props.children.props.onClick()
   expect(onCreateMigrationClick.calledOnce).toBe(true)
 })
 
@@ -86,12 +86,12 @@ it('has `Create migration` button disabled if endpoint is missing and last statu
     executions: [...item.executions, { id: 'execution-4', status: 'COMPLETED', created_at: new Date() }],
   }
   let wrapper = wrap({ endpoints, item: newItem, page: '' })
-  expect((wrapper.find('MainDetails').prop('bottomControls').props.children[0].props.disabled)).toBe(true)
+  expect((wrapper.find('MainDetails').prop('bottomControls').props.children[0].props.children.props.disabled)).toBe(true)
 })
 
 it('dispatches delete click', () => {
   let onDeleteReplicaClick = sinon.spy()
   let wrapper = wrap({ endpoints, item, page: '', onDeleteReplicaClick })
-  wrapper.find('MainDetails').prop('bottomControls').props.children[1].props.onClick()
+  wrapper.find('MainDetails').prop('bottomControls').props.children[1].props.children[1].props.onClick()
   expect(onDeleteReplicaClick.calledOnce).toBe(true)
 })

+ 24 - 0
src/components/pages/ReplicaDetailsPage/ReplicaDetailsPage.jsx

@@ -73,6 +73,7 @@ class ReplicaDetailsPage extends React.Component {
       showMigrationModal: false,
       showDeleteExecutionConfirmation: false,
       showDeleteReplicaConfirmation: false,
+      showDeleteReplicaDisksConfirmation: false,
       confirmationItem: null,
       showCancelConfirmation: false,
     }
@@ -149,6 +150,10 @@ class ReplicaDetailsPage extends React.Component {
     this.setState({ showDeleteReplicaConfirmation: true })
   }
 
+  handleDeleteReplicaDisksClick() {
+    this.setState({ showDeleteReplicaDisksConfirmation: true })
+  }
+
   handleDeleteReplicaConfirmation() {
     this.setState({ showDeleteReplicaConfirmation: false })
     window.location.href = '/#/replicas'
@@ -159,6 +164,16 @@ class ReplicaDetailsPage extends React.Component {
     this.setState({ showDeleteReplicaConfirmation: false })
   }
 
+  handleDeleteReplicaDisksConfirmation() {
+    this.setState({ showDeleteReplicaDisksConfirmation: false })
+    ReplicaActions.deleteDisks(this.props.replicaStore.replicaDetails.id)
+    window.location.href = `/#/replica/executions/${this.props.replicaStore.replicaDetails.id}`
+  }
+
+  handleCloseDeleteReplicaDisksConfirmation() {
+    this.setState({ showDeleteReplicaDisksConfirmation: false })
+  }
+
   handleCloseMigrationModal() {
     this.setState({ showMigrationModal: false })
   }
@@ -239,6 +254,7 @@ class ReplicaDetailsPage extends React.Component {
             onExecuteClick={() => { this.handleActionButtonClick() }}
             onCreateMigrationClick={() => { this.handleCreateMigrationClick() }}
             onDeleteReplicaClick={() => { this.handleDeleteReplicaClick() }}
+            onDeleteReplicaDisksClick={() => { this.handleDeleteReplicaDisksClick() }}
             onAddScheduleClick={schedule => { this.handleAddScheduleClick(schedule) }}
             onScheduleChange={(scheduleId, data) => { this.handleScheduleChange(scheduleId, data) }}
             onScheduleRemove={scheduleId => { this.handleScheduleRemove(scheduleId) }}
@@ -280,6 +296,14 @@ class ReplicaDetailsPage extends React.Component {
           onConfirmation={() => { this.handleDeleteReplicaConfirmation() }}
           onRequestClose={() => { this.handleCloseDeleteReplicaConfirmation() }}
         />
+        <AlertModal
+          isOpen={this.state.showDeleteReplicaDisksConfirmation}
+          title="Delete Replica Disks?"
+          message="Are you sure you want to delete this replica's disks?"
+          extraMessage="Deleting Coriolis Replica Disks is permanent!"
+          onConfirmation={() => { this.handleDeleteReplicaDisksConfirmation() }}
+          onRequestClose={() => { this.handleCloseDeleteReplicaDisksConfirmation() }}
+        />
         <AlertModal
           isOpen={this.state.showCancelConfirmation}
           title="Cancel Execution?"

+ 14 - 0
src/sources/ReplicaSource.js

@@ -179,6 +179,20 @@ class ReplicaSource {
       }).then(() => { resolve(replicaId) }, reject).catch(reject)
     })
   }
+
+  static deleteDisks(replicaId) {
+    return new Promise((resolve, reject) => {
+      let projectId = cookie.get('projectId')
+
+      Api.sendAjaxRequest({
+        url: `${servicesUrl.coriolis}/${projectId}/replicas/${replicaId}/actions`,
+        method: 'POST',
+        data: { 'delete-disks': null },
+      }).then(response => {
+        resolve(response.data.execution)
+      }, reject).catch(reject)
+    })
+  }
 }
 
 export default ReplicaSource

+ 22 - 11
src/stores/ReplicaStore.js

@@ -16,6 +16,23 @@ import alt from '../alt'
 import ReplicaActions from '../actions/ReplicaActions'
 import NotificationActions from '../actions/NotificationActions'
 
+class ReplicaStoreUtils {
+  static addExecutionToReplica({ replicaStore, replicaId, execution }) {
+    let executions = [execution]
+
+    if (replicaStore.replicaDetails.id === replicaId) {
+      if (replicaStore.replicaDetails.executions) {
+        executions = [...replicaStore.replicaDetails.executions, execution]
+      }
+
+      replicaStore.replicaDetails = {
+        ...replicaStore.replicaDetails,
+        executions,
+      }
+    }
+  }
+}
+
 class ReplicaStore {
   constructor() {
     this.replicas = []
@@ -39,6 +56,7 @@ class ReplicaStore {
       handleExecuteSuccess: ReplicaActions.EXECUTE_SUCCESS,
       handleDeleteExecutionSuccess: ReplicaActions.DELETE_EXECUTION_SUCCESS,
       handleDeleteSuccess: ReplicaActions.DELETE_SUCCESS,
+      handleDeleteDisksSuccess: ReplicaActions.DELETE_DISKS_SUCCESS,
       handleCancelExecutionSuccess: ReplicaActions.CANCEL_EXECUTION_SUCCESS,
       handleClearDetails: ReplicaActions.CLEAR_DETAILS,
     })
@@ -111,18 +129,11 @@ class ReplicaStore {
   }
 
   handleExecuteSuccess({ replicaId, execution }) {
-    let executions = [execution]
-
-    if (this.replicaDetails.id === replicaId) {
-      if (this.replicaDetails.executions) {
-        executions = [...this.replicaDetails.executions, execution]
-      }
+    ReplicaStoreUtils.addExecutionToReplica({ replicaStore: this, replicaId, execution })
+  }
 
-      this.replicaDetails = {
-        ...this.replicaDetails,
-        executions,
-      }
-    }
+  handleDeleteDisksSuccess({ replicaId, execution }) {
+    ReplicaStoreUtils.addExecutionToReplica({ replicaStore: this, replicaId, execution })
   }
 
   handleDeleteExecutionSuccess({ replicaId, executionId }) {