Sfoglia il codice sorgente

Better handling of multi. replicas creation errors

Now, when creating multiple replicas / migrations, there's a graceful
handling of creation errors.

Along with the error messages for each failed replica creation, a
summary alert is also shown with the total number of created and failed
replicas.

If there's at least 1 error, the user is no longer redirected from the
Wizard page.
Sergiu Miclea 6 anni fa
parent
commit
afafeab72f

+ 5 - 7
src/components/pages/WizardPage/WizardPage.jsx

@@ -134,7 +134,7 @@ class WizardPage extends React.Component<Props, State> {
 
   async handleCreationSuccess(items: MainItem[]) {
     let typeLabel = this.state.type.charAt(0).toUpperCase() + this.state.type.substr(1)
-    notificationStore.alert(`${typeLabel} was succesfully created`, 'success')
+    notificationStore.alert(`${typeLabel}${items.length > 1 ? 's' : ''} was succesfully created`, 'success')
     let schedulePromise = Promise.resolve()
 
     if (this.state.type === 'replica') {
@@ -483,20 +483,18 @@ class WizardPage extends React.Component<Props, State> {
   async createMultiple() {
     let typeLabel = this.state.type.charAt(0).toUpperCase() + this.state.type.substr(1)
     notificationStore.alert(`Creating ${typeLabel}s ...`)
-    await wizardStore.createMultiple(
+    let success = await wizardStore.createMultiple(
       this.state.type,
       wizardStore.data,
       wizardStore.defaultStorage,
       wizardStore.storageMap,
       wizardStore.uploadedUserScripts,
     )
-    let items = wizardStore.createdItems
-    if (!items) {
-      notificationStore.alert(`${typeLabel}s couldn't be created`, 'error')
+    if (success && wizardStore.createdItems) {
+      this.handleCreationSuccess(wizardStore.createdItems.filter(Boolean))
+    } else {
       this.setState({ nextButtonDisabled: false })
-      return
     }
-    this.handleCreationSuccess(items)
   }
 
   async createSingle() {

+ 7 - 7
src/sources/WizardSource.js

@@ -15,7 +15,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 // @flow
 
 import Api from '../utils/ApiCaller'
-import notificationStore from '../stores/NotificationStore'
 import { OptionsSchemaPlugin } from '../plugins/endpoint'
 
 import DomUtils from '../utils/DomUtils'
@@ -77,22 +76,23 @@ class WizardSource {
     defaultStorage: ?string,
     storageMap: StorageMap[],
     uploadedUserScripts: InstanceScript[]
-  ): Promise<MainItem[]> {
+  ) {
     if (!data.selectedInstances) {
       throw new Error('No selected instances')
     }
     let mainItems = await Promise.all(data.selectedInstances.map(async instance => {
       let newData = { ...data }
       newData.selectedInstances = [instance]
+      let mainItem: ?MainItem = null
       try {
-        let mainItem: MainItem = await this.create(type, newData, defaultStorage, storageMap, uploadedUserScripts)
+        mainItem = await this.create(type, newData, defaultStorage, storageMap, uploadedUserScripts)
+      } finally {
+        // If an there's an error with the request, return null, don't break the loop.
+        // eslint-disable-next-line no-unsafe-finally
         return mainItem
-      } catch (err) {
-        notificationStore.alert(`Error while creating ${type} for instance ${instance.name}`, 'error')
-        return null
       }
     }))
-    return mainItems.filter(Boolean).map(i => i)
+    return mainItems
   }
 
   setUrlState(data: any) {

+ 35 - 4
src/stores/WizardStore.js

@@ -25,6 +25,7 @@ import type { StorageMap } from '../types/Endpoint'
 import type { Schedule } from '../types/Schedule'
 import { wizardPages } from '../constants'
 import source from '../sources/WizardSource'
+import notificationStore from './NotificationStore'
 
 const updateOptions = (oldOptions: ?{ [string]: mixed }, data: { field: Field, value: any }) => {
   let options = { ...oldOptions }
@@ -59,7 +60,7 @@ class WizardStore {
   @observable currentPage: WizardPage = wizardPages[0]
   @observable createdItem: ?MainItem = null
   @observable creatingItem: boolean = false
-  @observable createdItems: ?MainItem[] = null
+  @observable createdItems: ?Array<?MainItem> = null
   @observable creatingItems: boolean = false
   @observable uploadedUserScripts: InstanceScript[] = []
 
@@ -180,12 +181,42 @@ class WizardStore {
     defaultStorage: ?string,
     storageMap: StorageMap[],
     uploadedUserScripts: InstanceScript[]
-  ): Promise<void> {
+  ): Promise<boolean> {
     this.creatingItems = true
 
     try {
-      let items: MainItem[] = await source.createMultiple(type, data, defaultStorage, storageMap, uploadedUserScripts)
-      runInAction(() => { this.createdItems = items })
+      let items = await source.createMultiple(type, data, defaultStorage, storageMap, uploadedUserScripts)
+      let nullItemsCount = items.filter(i => i === null).length
+      if (items && nullItemsCount === 0) {
+        runInAction(() => { this.createdItems = items })
+        return true
+      }
+      let errorMessage = null
+      let alertOptions = null
+      if (!items || nullItemsCount === items.length) {
+        errorMessage = `No ${type}s could be created`
+      } else {
+        errorMessage = `Some ${type}s couldn't be created.`
+        alertOptions = {
+          action: {
+            label: 'View details',
+            callback: () => ({
+              request: {
+                url: '[MULTIPLE]',
+                method: 'POST',
+                message: `Error creating some ${type}s`,
+                data: {
+                  created: items.filter(Boolean).length,
+                  failed: nullItemsCount,
+                },
+              },
+              error: { status, message: errorMessage },
+            }),
+          },
+        }
+      }
+      notificationStore.alert(errorMessage, 'error', alertOptions)
+      return false
     } finally {
       runInAction(() => { this.creatingItems = false })
     }

+ 1 - 1
src/types/NotificationItem.js

@@ -24,7 +24,7 @@ export type AlertInfoOptions = {
 }
 
 export type AlertInfo = {
-  options?: AlertInfoOptions,
+  options?: ?AlertInfoOptions,
   message: string,
   title?: string,
   id?: string,