Browse Source

ref(forms): Make `mapFormErrors` a form model option (#47120)

This allows specialization of form error response mapping without
needing to extend the FormModel class
Evan Purkhiser 1 year ago
parent
commit
cd5438bbda

+ 9 - 5
static/app/components/forms/model.tsx

@@ -34,6 +34,10 @@ export type FormOptions = {
    * Initial form data
    */
   initialData?: Record<string, FieldValue>;
+  /**
+   * Custom transformer function for use with the error response
+   */
+  mapFormErrors?: (responseJson: any) => any;
   /**
    * Callback triggered when a field changes value
    */
@@ -746,12 +750,12 @@ class FormModel {
 
   submitError(err: {responseJSON?: any}) {
     this.formState = FormState.ERROR;
-    this.formErrors = this.mapFormErrors(err.responseJSON);
-    this.handleErrorResponse({responseJSON: this.formErrors});
-  }
 
-  mapFormErrors(responseJSON?: any) {
-    return responseJSON;
+    this.formErrors = this.options.mapFormErrors
+      ? this.options.mapFormErrors(err.responseJSON)
+      : err.responseJSON;
+
+    this.handleErrorResponse({responseJSON: this.formErrors});
   }
 }
 

+ 28 - 28
static/app/views/settings/organizationDeveloperSettings/sentryApplicationDetails.tsx

@@ -84,6 +84,33 @@ const getResourceFromScope = (scope: Scope): Resource | undefined => {
   return undefined;
 };
 
+/**
+ * We need to map the API response errors to the actual form fields.
+ * We do this by pulling out scopes and mapping each scope error to the correct input.
+ * @param {Object} responseJSON
+ */
+const mapFormErrors = (responseJSON?: any) => {
+  if (!responseJSON) {
+    return responseJSON;
+  }
+  const formErrors = omit(responseJSON, ['scopes']);
+  if (responseJSON.scopes) {
+    responseJSON.scopes.forEach((message: string) => {
+      // find the scope from the error message of a specific format
+      const matches = message.match(/Requested permission of (\w+:\w+)/);
+      if (matches) {
+        const scope = matches[1];
+        const resource = getResourceFromScope(scope as Scope);
+        // should always match but technically resource can be undefined
+        if (resource) {
+          formErrors[`${resource}--permission`] = [message];
+        }
+      }
+    });
+  }
+  return formErrors;
+};
+
 class SentryAppFormModel extends FormModel {
   /**
    * Filter out Permission input field values.
@@ -106,33 +133,6 @@ class SentryAppFormModel extends FormModel {
       return data;
     }, {});
   }
-
-  /**
-   * We need to map the API response errors to the actual form fields.
-   * We do this by pulling out scopes and mapping each scope error to the correct input.
-   * @param {Object} responseJSON
-   */
-  mapFormErrors(responseJSON?: any) {
-    if (!responseJSON) {
-      return responseJSON;
-    }
-    const formErrors = omit(responseJSON, ['scopes']);
-    if (responseJSON.scopes) {
-      responseJSON.scopes.forEach((message: string) => {
-        // find the scope from the error message of a specific format
-        const matches = message.match(/Requested permission of (\w+:\w+)/);
-        if (matches) {
-          const scope = matches[1];
-          const resource = getResourceFromScope(scope as Scope);
-          // should always match but technically resource can be undefined
-          if (resource) {
-            formErrors[`${resource}--permission`] = [message];
-          }
-        }
-      });
-    }
-    return formErrors;
-  }
 }
 
 type Props = RouteComponentProps<{appSlug?: string}, {}> & {
@@ -145,7 +145,7 @@ type State = AsyncView['state'] & {
 };
 
 class SentryApplicationDetails extends AsyncView<Props, State> {
-  form = new SentryAppFormModel();
+  form = new SentryAppFormModel({mapFormErrors});
 
   getDefaultState(): State {
     return {