@apollo/client#ApolloError TypeScript Examples

The following examples show how to use @apollo/client#ApolloError. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: entityRestore.ts    From jmix-frontend with Apache License 2.0 6 votes vote down vote up
export function useEntityRestore(restoreEntitesVariables: RestoreEntitiesVariables, onCompleteCallback?: () => Promise<any>) {
  const intl = useIntl();
  const {className, ids} = restoreEntitesVariables;
  const [restoreEntities] = useMutation<RestoreData, RestoreEntitiesVariables>(
    gqlRestoreEntity,
    {
      fetchPolicy: "no-cache"
    }
  );

  const restore = () => {
    restoreEntities({
      variables: {
        className, 
        ids
      }
    }).then(({data}) => {
      if(data != null) {
        restoreEntitesHandler(data, intl, onCompleteCallback);
      }
    }).catch(({message}: ApolloError) => {
      restoreEntitesErrorHandler(message, intl);
    })
  };

  return restore;
}
Example #2
Source File: PaymentsPageContent.tsx    From condo with MIT License 6 votes vote down vote up
function renderError (error: ApolloError | string) {
    if (isString(error)) {
        return (
            <BasicEmptyListView>
                <Typography.Title level={3}>
                    {error}
                </Typography.Title>
            </BasicEmptyListView>
        )
    }

    return (
        <BasicEmptyListView>
            <Typography.Title level={3}>
                {error.name}
            </Typography.Title>
            <Typography.Text>
                {error.message}
            </Typography.Text>
        </BasicEmptyListView>
    )
}
Example #3
Source File: extractBeanValidationErrors.ts    From jmix-frontend with Apache License 2.0 6 votes vote down vote up
export function extractBeanValidationErrors(apolloError?: ApolloError): JmixServerValidationErrors | undefined {
  if (apolloError == null) {
    return undefined;
  }

  const constraintViolations = apolloError
    ?.graphQLErrors
    ?.[0]
    ?.extensions
    ?.constraintViolations;

  if (constraintViolations == null) {
    return undefined;
  }

  const fieldErrors = new Map<string, string[]>();
  const globalErrors: string[] = [];

  constraintViolations.forEach((violation: JmixConstraintViolation) => {
    // Global error, e.g. cross-validation
    if (violation.path === '') {
      globalErrors.push(violation.message);
      return;
    }

    // Field error
    const messages = fieldErrors.get(violation.path) ?? [];
    messages.push(violation.message);
    fieldErrors.set(violation.path, messages);
  });

  return {
    fieldErrors,
    globalErrors
  };
}
Example #4
Source File: useQuery.ts    From lexicon with MIT License 6 votes vote down vote up
export function useQuery<TData, TVariables = OperationVariables>(
  query: DocumentNode,
  options?: QueryHookOptions<TData, TVariables>,
  errorAlert: ErrorAlertOptionType = 'SHOW_ALERT',
): QueryResult<TData, TVariables> {
  const onErrorDefault = (error: ApolloError) => {
    errorHandlerAlert(error);
  };

  const { fetchPolicy = 'cache-and-network', ...others } = options ?? {};

  const {
    onError = errorAlert === 'SHOW_ALERT' ? onErrorDefault : undefined,
    nextFetchPolicy = fetchPolicy === 'cache-and-network'
      ? 'cache-first'
      : undefined,
    notifyOnNetworkStatusChange = fetchPolicy === 'network-only',
    ...otherOptions
  } = others;

  let queryResult = useQueryBase<TData, TVariables>(query, {
    fetchPolicy,
    nextFetchPolicy,
    notifyOnNetworkStatusChange,
    onError,
    ...otherOptions,
  });

  return queryResult;
}
Example #5
Source File: useMutation.ts    From lexicon with MIT License 6 votes vote down vote up
export function useMutation<TData, TVariables = OperationVariables>(
  query: DocumentNode,
  options?: MutationHookOptions<TData, TVariables>,
  errorAlert: ErrorAlertOptionType = 'SHOW_ALERT',
): MutationTuple<TData, TVariables> {
  const { navigate } = useNavigation<TabNavProp<'Home'>>();

  const onErrorDefault = (error: ApolloError) => {
    errorHandlerAlert(error, navigate);
  };

  const { ...others } = options ?? {};

  const {
    onError = errorAlert === 'SHOW_ALERT' ? onErrorDefault : undefined,
    ...otherOptions
  } = others;

  let [mutationFunction, mutationResult] = useMutationBase<TData, TVariables>(
    query,
    {
      onError,
      ...otherOptions,
    },
  );

  return [mutationFunction, mutationResult];
}
Example #6
Source File: useLazyQuery.ts    From lexicon with MIT License 6 votes vote down vote up
export function useLazyQuery<TData, TVariables = OperationVariables>(
  query: DocumentNode,
  options?: LazyQueryHookOptions<TData, TVariables>,
  errorAlert: ErrorAlertOptionType = 'SHOW_ALERT',
): QueryTuple<TData, TVariables> {
  const onErrorDefault = (error: ApolloError) => {
    errorHandlerAlert(error);
  };

  const { fetchPolicy = 'cache-and-network', ...others } = options ?? {};

  const {
    onError = errorAlert === 'SHOW_ALERT' ? onErrorDefault : undefined,
    nextFetchPolicy = fetchPolicy === 'cache-and-network'
      ? 'cache-first'
      : undefined,
    notifyOnNetworkStatusChange = fetchPolicy === 'network-only',
    ...otherOptions
  } = others;

  let [queryFunction, queryResult] = useLazyQueryBase<TData, TVariables>(
    query,
    {
      fetchPolicy,
      nextFetchPolicy,
      notifyOnNetworkStatusChange,
      onError,
      ...otherOptions,
    },
  );

  return [queryFunction, queryResult];
}
Example #7
Source File: errorHandler.ts    From lexicon with MIT License 6 votes vote down vote up
export function errorHandler(
  error: ApolloError,
  isFiltered = false,
  isTranslated = true,
): string {
  let errorMsg: string;

  if (error.networkError) {
    errorMsg = 'Please connect to a network';
  } else if (error.message === 'Not found or private.') {
    errorMsg = 'Not found or private';
  } else {
    errorMsg = isFiltered
      ? 'Something unexpected happened. Please try again'
      : error.message;
  }

  return isTranslated ? t('{errorMsg}', { errorMsg }) : errorMsg;
}
Example #8
Source File: error.ts    From amplication with Apache License 2.0 6 votes vote down vote up
export function formatError(error: Error | undefined): string | undefined {
  if (error === undefined) {
    return undefined;
  }
  if ((error as ApolloError).graphQLErrors) {
    // show the first error
    /**@todo: return multiple errors */
    const [gqlError] = (error as ApolloError).graphQLErrors;
    if (gqlError && gqlError.message) {
      return gqlError.message;
    }
  }
  return String(error);
}
Example #9
Source File: base-user-apollo.service.ts    From svvs with MIT License 6 votes vote down vote up
// eslint-disable-next-line @typescript-eslint/no-unused-vars
  loadUser(queryParams: Record<string, unknown> = {}): TApolloResponse<IUser> {
    return this.apollo
      .query<{ user: IUser}>( {query: UserQieries.usersRequest.query} )
      .pipe(
        map(result => extractApolloResponse(result, UserQieries.usersRequest.keys)),
        catchError((error: ApolloError) => throwError(error))
      )
  }
Example #10
Source File: persistEntity.ts    From jmix-frontend with Apache License 2.0 5 votes vote down vote up
export function persistEntity<
  TEntity = unknown,
  TData extends Record<string, any> = Record<string, any>,
  TVariables = unknown
  >(
  upsertItem: GraphQLMutationFn<TData, TVariables>,
  upsertInputName: string,
  updatedEntity: TEntity,
  updateResultName: string,
  listQueryName: string,
  entityName: string,
  isNewEntity: boolean,
  goToParentScreen: () => void,
  metadata: Metadata,
  persistEntityCallbacks?: PersistEntityCallbacks
) {
  upsertItem({
    variables: {
      [upsertInputName]: jmixFront_to_jmixGraphQL(updatedEntity, entityName, metadata)
    } as any,
    update(cache: ApolloCache<TData>, result: FetchResult<TData>) {
      const updateResult = result.data?.[updateResultName];
      // Reflect the update in Apollo cache
      cache.modify({
        fields: {
          [listQueryName](existingRefs = []) {
            const updatedItemRef = cache.writeFragment({
              id: `${entityName}:${updateResult.id}`,
              data: updatedEntity,
              fragment: gql(`
                      fragment New_${dollarsToUnderscores(entityName)} on ${dollarsToUnderscores(entityName)} {
                        id
                        type
                      }
                    `)
            });
            return [...existingRefs, updatedItemRef];
          }
        }
      });
    }
  }).then(action(({errors}: FetchResult<TData, Record<string, any>, Record<string, any>>) => {
    if (errors == null || errors.length === 0) {
      isNewEntity ? persistEntityCallbacks?.onCreate?.() : persistEntityCallbacks?.onEdit?.()
      goToParentScreen();
    } else {
      console.error(errors);
      persistEntityCallbacks?.onError?.();
    }
  }))
    .catch((e: Error | ApolloError) => {
      const constraintViolations = (e as ApolloError)
        ?.graphQLErrors
        ?.[0]
        ?.extensions
        ?.constraintViolations;
      if (constraintViolations != null) {
        return; // Bean validation error
      }

      console.error(e);
      persistEntityCallbacks?.onError?.();
    });
}
Example #11
Source File: errorHandler.ts    From lexicon with MIT License 5 votes vote down vote up
export function errorHandlerAlert(
  error: ApolloError | string,
  navigate?: Function,
  isFiltered = false,
) {
  let errorMsg;

  if (typeof error === 'string') {
    errorMsg = error;
  } else {
    errorMsg = errorHandler(error, isFiltered, false);
  }

  let errorMsgi8n = t('{errorMsg}', { errorMsg });

  switch (errorMsg) {
    case LoginError:
      Alert.alert(t('Please Log In'), errorMsgi8n, [
        { text: t('Close') },
        {
          text: t('Log In'),
          onPress: () => (navigate ? navigate('Login') : undefined),
        },
      ]);
      break;
    case UsedTitleError:
      Alert.alert(
        t('Title Already Exists'),
        t(
          'A Post with this title has already been created. Please use a different title.',
        ),
        [{ text: t('Got it') }],
      );
      break;
    case EditPostError:
      Alert.alert(t('Unable to Edit'), errorMsgi8n, [{ text: t('Got it') }]);
      break;
    case ChangeUsernameError:
      Alert.alert(t('Username Unavailable'), errorMsgi8n, [
        { text: t('Got it') },
      ]);
      break;
    default:
      Alert.alert(t('Error'), errorMsgi8n, [{ text: t('Got it') }]);
  }
}
Example #12
Source File: ErrorText.tsx    From graphql-ts-client with MIT License 5 votes vote down vote up
ErrorText: FC<{
    error: ApolloError
}> = memo(({error}) => {

    const serverErrors = (error.networkError as any).result?.errors as {readonly message: string}[] | undefined;
    return (
        <div className={ERROR_CSS}>
            <div className={FORM_CSS}>
                {
                    serverErrors && 
                    <ul>
                        {
                            serverErrors.map((serverError, index) =>
                                <li key={index}>{serverError.message}</li> 
                            )
                        }
                    </ul>
                }
                {
                    serverErrors === undefined && error.networkError && 
                    <div>
                        <div>Network error</div>
                        <div>{error.networkError.message}</div>
                    </div>
                }
                {
                    error.clientErrors.length !== 0 && 
                    <div>
                        <div>Client errors</div>
                        <div>
                            <ul>
                                {
                                    error.clientErrors.map(ce =>
                                        <li>{ce.message}</li>
                                    )
                                }
                            </ul>
                        </div>
                    </div>
                }
                {
                    error.graphQLErrors.length !== 0 && 
                    <div>
                        <div>GraphQL errors</div>
                        <div>
                            <ul>
                                {
                                    error.graphQLErrors.map(ce =>
                                        <li>{ce.message}</li>
                                    )
                                }
                            </ul>
                        </div>
                    </div>
                }
            </div>
        </div>
    );
})
Example #13
Source File: users-facade.interface.ts    From svvs with MIT License 5 votes vote down vote up
/**
   * User load error observer
   */
  userLoadFailure$: Observable<ApolloError>
Example #14
Source File: users.actions.ts    From svvs with MIT License 5 votes vote down vote up
loadUserFailure = createAction('[Users] Load User Failure', payload<ApolloError>())
Example #15
Source File: base-auth-apollo.service.ts    From svvs with MIT License 5 votes vote down vote up
signOut(queryParams?: Record<string, unknown>): TApolloResponse<void> {
    return this.apollo
      .query<{logout: boolean}>({query: AuthQueries.logoutRequest.query})
      .pipe(
        map(result => null),
        catchError((error: ApolloError) => throwError(error))
      )
  }
Example #16
Source File: base-auth-apollo.service.ts    From svvs with MIT License 5 votes vote down vote up
signIn(payload: ISignAuthPayload, queryParams: Record<string, unknown> = {}): TApolloResponse<ISignAuthResponse> {
    return this.apollo
      .query<{user: IUser}>({query: AuthQueries.loginRequest.query, variables: payload})
      .pipe(
        map(result => extractApolloResponse(result, AuthQueries.loginRequest.keys)),
        catchError((error: ApolloError) => throwError(error))
      )
  }
Example #17
Source File: auth-facade.interface.ts    From svvs with MIT License 5 votes vote down vote up
/**
   * SignOut error observer
   */
  signOutError$: Observable<ApolloError>
Example #18
Source File: auth-facade.interface.ts    From svvs with MIT License 5 votes vote down vote up
/**
   * SignInError observer
   */
  signInError$: Observable<ApolloError>
Example #19
Source File: auth.actions.ts    From svvs with MIT License 5 votes vote down vote up
signOutFailure = createAction('[Auth] SignOut error', payload<ApolloError>())
Example #20
Source File: auth.actions.ts    From svvs with MIT License 5 votes vote down vote up
signInFailure = createAction('[Auth] SignIn error', payload<ApolloError>())
Example #21
Source File: extractBeanValidationErrors.ts    From jmix-frontend with Apache License 2.0 5 votes vote down vote up
export function useExtractBeanValidationErrors(apolloError?: ApolloError): JmixServerValidationErrors | undefined {
  return useMemo(() => extractBeanValidationErrors(apolloError), [apolloError]);
}
Example #22
Source File: FormLayout.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
FormLayout: React.SFC<FormLayoutProps> = ({
  match,
  deleteItemQuery,
  states,
  setStates,
  validationSchema,
  listItemName,
  dialogMessage,
  formFields,
  redirectionLink,
  listItem,
  getItemQuery,
  createItemQuery,
  updateItemQuery,
  additionalQuery = null,
  defaultAttribute = null,
  additionalAction,
  icon,
  idType = 'id',
  additionalState,
  title,
  linkParameter = null,
  cancelLink = null,
  languageSupport = true,
  setPayload,
  advanceSearch,
  cancelAction,
  button = 'Save',
  type,
  afterSave,
  afterDelete,
  refetchQueries,
  redirect = true,
  getLanguageId,
  backLinkButton,
  isAttachment = false,
  getMediaId,
  customStyles = null,
  customHandler,
  copyNotification = '',
  showPreviewButton = false,
  onPreviewClick = () => {},
  getQueryFetchPolicy = 'cache-first',
  saveOnPageChange = true,
}: FormLayoutProps) => {
  const [showDialog, setShowDialog] = useState(false);
  const [formSubmitted, setFormSubmitted] = useState(false);
  const [languageId, setLanguageId] = useState('');
  const [formCancelled, setFormCancelled] = useState(false);
  const [action, setAction] = useState(false);
  const [link, setLink] = useState(undefined);
  const [deleted, setDeleted] = useState(false);
  const [saveClick, onSaveClick] = useState(false);
  const [isLoadedData, setIsLoadedData] = useState(false);
  const [customError, setCustomError] = useState<any>(null);

  const { t } = useTranslation();

  const capitalListItemName = listItemName[0].toUpperCase() + listItemName.slice(1);
  let item: any = null;
  const itemId = match.params.id ? match.params.id : false;
  let variables: any = itemId ? { [idType]: itemId } : false;

  const [deleteItem] = useMutation(deleteItemQuery, {
    onCompleted: () => {
      setNotification(`${capitalListItemName} deleted successfully`);
      setDeleted(true);
    },
    awaitRefetchQueries: true,
    refetchQueries: [
      {
        query: SEARCH_QUERY,
        variables: SEARCH_QUERY_VARIABLES,
      },
    ],
  });

  // get the organization for current user and have languages option set to that.

  const organization = useQuery(USER_LANGUAGES, {
    onCompleted: (data: any) => {
      if (!itemId) {
        setLanguageId(data.currentUser.user.organization.defaultLanguage.id);
      }
    },
  });
  if (listItem === 'credential') {
    variables = match.params.shortcode ? { shortcode: match.params.shortcode } : false;
  }

  const { loading, error } = useQuery(getItemQuery, {
    variables,
    skip: !itemId,
    fetchPolicy: getQueryFetchPolicy,
    onCompleted: (data) => {
      if (data) {
        item = data[listItem] ? data[listItem][listItem] : data[Object.keys(data)[0]][listItem];
        if (item) {
          setIsLoadedData(true);
          setLink(data[listItem] ? data[listItem][listItem][linkParameter] : item.linkParameter);
          setStates(item);
          setLanguageId(languageSupport ? item.language.id : null);
        }
      }
    },
  });
  const camelCaseItem = listItem[0].toUpperCase() + listItem.slice(1);

  const [updateItem] = useMutation(updateItemQuery, {
    onCompleted: (data) => {
      let itemUpdatedObject: any = Object.keys(data)[0];
      itemUpdatedObject = data[itemUpdatedObject];
      const updatedItem = itemUpdatedObject[listItem];
      const { errors } = itemUpdatedObject;

      if (errors) {
        if (customHandler) {
          customHandler(errors);
        } else {
          setErrorMessage(errors[0]);
        }
      } else if (updatedItem && typeof updatedItem.isValid === 'boolean' && !updatedItem.isValid) {
        if (customError) {
          // this is a custom error for extensions. We need to move this out of this component
          const codeErrors = { code: 'Failed to compile code. Please check again' };
          customError.setErrors(codeErrors);
        }
      } else {
        if (type === 'copy') setLink(updatedItem[linkParameter]);
        if (additionalQuery) {
          additionalQuery(itemId);
        }

        if (saveOnPageChange || saveClick) {
          setFormSubmitted(true);
          // display successful message after update
          let message = `${capitalListItemName} edited successfully!`;
          if (type === 'copy') {
            message = copyNotification;
          }
          setNotification(message);
        } else {
          setNotification('Your changes have been autosaved');
        }
        // emit data after save
        if (afterSave) {
          afterSave(data, saveClick);
        }
      }
      onSaveClick(false);
    },
    onError: (e: ApolloError) => {
      onSaveClick(false);
      setErrorMessage(e);
      return null;
    },
    refetchQueries: () => {
      if (refetchQueries)
        return refetchQueries.map((refetchQuery: any) => ({
          query: refetchQuery.query,
          variables: refetchQuery.variables,
        }));
      return [];
    },
  });

  const [createItem] = useMutation(createItemQuery, {
    onCompleted: (data) => {
      let itemCreatedObject: any = `create${camelCaseItem}`;
      itemCreatedObject = data[itemCreatedObject];
      const itemCreated = itemCreatedObject[listItem];

      const { errors } = itemCreatedObject;
      if (errors) {
        if (customHandler) {
          customHandler(errors);
        } else {
          setErrorMessage(errors[0]);
        }
      } else if (itemCreated && typeof itemCreated.isValid === 'boolean' && !itemCreated.isValid) {
        if (customError) {
          const codeErrors = { code: 'Failed to compile code. Please check again' };
          customError.setErrors(codeErrors);
        }
      } else {
        if (additionalQuery) {
          additionalQuery(itemCreated.id);
        }
        if (!itemId) setLink(itemCreated[linkParameter]);
        if (saveOnPageChange || saveClick) {
          setFormSubmitted(true);
          // display successful message after create
          setNotification(`${capitalListItemName} created successfully!`);
        } else {
          setNotification('Your changes have been autosaved');
        }
        // emit data after save
        if (afterSave) {
          afterSave(data, saveClick);
        }
      }
      setIsLoadedData(true);
      onSaveClick(false);
    },
    refetchQueries: () => {
      if (refetchQueries)
        return refetchQueries.map((refetchQuery: any) => ({
          query: refetchQuery.query,
          variables: refetchQuery.variables,
        }));

      return [];
    },
    onError: (e: ApolloError) => {
      onSaveClick(false);
      setErrorMessage(e);
      return null;
    },
  });

  if (loading) return <Loading />;
  if (error) {
    setErrorMessage(error);
    return null;
  }
  const performTask = (payload: any) => {
    if (itemId) {
      if (isLoadedData) {
        let idKey = idType;
        let idVal = itemId;

        /**
         * When idType is organizationId
         * We are updating billing for given organization
         * since match.params.id is orgId we want billing
         * id to update billing details
         */
        const payloadBody = { ...payload };
        if (idType === 'organizationId') {
          idKey = 'id';
          idVal = payloadBody.billingId;
          // Clearning unnecessary fields
          delete payloadBody.billingId;
        }

        updateItem({
          variables: {
            [idKey]: idVal,
            input: payloadBody,
          },
        });
      } else {
        createItem({
          variables: {
            input: payload,
          },
        });
      }
    } else {
      createItem({
        variables: {
          input: payload,
        },
      });
    }
  };
  const saveHandler = ({ languageId: languageIdValue, ...itemData }: any) => {
    let payload = {
      ...itemData,
      ...defaultAttribute,
    };
    payload = languageSupport
      ? { ...payload, languageId: Number(languageIdValue) }
      : { ...payload };

    // create custom payload for searches
    if (setPayload) {
      payload = setPayload(payload);
      if (advanceSearch) {
        const data = advanceSearch(payload);

        if (data && data.heading && type === 'search') return;
      }
    }
    // remove fields from the payload that marked as skipPayload = true
    formFields.forEach((field: any) => {
      if (field.additionalState) {
        additionalState(payload[field.additionalState]);
      }
      if (field.convertToWhatsApp && payload[field.name]) {
        payload[field.name] = getPlainTextFromEditor(payload[field.name]);
      }
      if (field.skipPayload) {
        delete payload[field.name];
      }
    });
    // for template create media for attachment
    if (isAttachment && payload.type !== 'TEXT' && payload.type) {
      getMediaId(payload)
        .then((data: any) => {
          if (data) {
            const payloadCopy = payload;
            delete payloadCopy.attachmentURL;
            payloadCopy.messageMediaId = parseInt(data.data.createMessageMedia.messageMedia.id, 10);
            performTask(payloadCopy);
          }
        })
        .catch((e: any) => {
          setErrorMessage(e);
        });
    } else {
      performTask(payload);
    }
  };

  const cancelHandler = () => {
    // for chat screen searches
    if (type === 'search' || type === 'saveSearch') {
      advanceSearch('cancel');
      return;
    }
    if (cancelAction) {
      cancelAction();
    }
    setFormCancelled(true);
  };

  if (formSubmitted && redirect) {
    return <Redirect to={action ? `${additionalAction.link}/${link}` : `/${redirectionLink}`} />;
  }

  if (deleted) {
    if (afterDelete) {
      return <Redirect to={afterDelete.link} />;
    }
    return <Redirect to={`/${redirectionLink}`} />;
  }

  if (formCancelled) {
    return <Redirect to={cancelLink ? `/${cancelLink}` : `/${redirectionLink}`} />;
  }
  const validateLanguage = (value: any) => {
    if (value && getLanguageId) {
      getLanguageId(value);
    }
  };

  const languageOptions = organization.data
    ? organization.data.currentUser.user.organization.activeLanguages.slice()
    : [];
  // sort languages by their name
  languageOptions.sort((first: any, second: any) => (first.label > second.label ? 1 : -1));

  const language = languageSupport
    ? {
        component: Dropdown,
        name: 'languageId',
        placeholder: t('Language'),
        options: languageOptions,
        validate: validateLanguage,
        helperText: t('For more languages check settings or connect with your admin'),
      }
    : null;

  const formFieldItems = languageSupport ? [...formFields, language] : formFields;
  const deleteButton =
    itemId && !type ? (
      <Button
        variant="contained"
        color="secondary"
        className={styles.DeleteButton}
        onClick={() => setShowDialog(true)}
      >
        <DeleteIcon className={styles.DeleteIcon} />
        Remove
      </Button>
    ) : null;

  const onSaveButtonClick = (errors: any) => {
    if (Object.keys(errors).length > 0) {
      return;
    }
    onSaveClick(true);
  };

  const form = (
    <Formik
      enableReinitialize
      validateOnMount
      initialValues={{
        ...states,
        languageId,
      }}
      validationSchema={validationSchema}
      onSubmit={(itemData, { setErrors }) => {
        // when you want to show custom error on form field and error message is not coming from api
        setCustomError({ setErrors });
        saveHandler(itemData);
      }}
    >
      {({ errors, submitForm }) => (
        <Form className={[styles.Form, customStyles].join(' ')} data-testid="formLayout">
          {formFieldItems.map((field, index) => {
            const key = index;

            if (field.skip) {
              return null;
            }

            return (
              <React.Fragment key={key}>
                {field.label && (
                  <Typography variant="h5" className={styles.FieldLabel}>
                    {field.label}
                  </Typography>
                )}
                <Field key={key} {...field} onSubmit={submitForm} />
              </React.Fragment>
            );
          })}
          <div className={styles.Buttons}>
            <Button
              variant="contained"
              color="primary"
              onClick={() => {
                onSaveButtonClick(errors);
                submitForm();
              }}
              className={styles.Button}
              data-testid="submitActionButton"
              loading={saveClick}
            >
              {button}
            </Button>
            {additionalAction ? (
              <Button
                variant="outlined"
                color="primary"
                onClick={() => {
                  submitForm();
                  setAction(true);
                }}
                data-testid="additionalActionButton"
              >
                {additionalAction.label}
              </Button>
            ) : null}
            <Button
              variant="contained"
              color="default"
              onClick={cancelHandler}
              data-testid="cancelActionButton"
            >
              {t('Cancel')}
            </Button>
            {showPreviewButton && (
              <Button
                variant="contained"
                color="primary"
                onClick={onPreviewClick}
                className={styles.Button}
                data-testid="previewButton"
              >
                Preview
              </Button>
            )}
            {deleteButton}
          </div>
        </Form>
      )}
    </Formik>
  );

  const handleDeleteItem = () => {
    deleteItem({ variables: { id: itemId } });
  };

  let dialogBox;

  if (showDialog) {
    dialogBox = (
      <DialogBox
        title={`Are you sure you want to delete the ${listItemName}?`}
        handleOk={handleDeleteItem}
        handleCancel={() => setShowDialog(false)}
        colorOk="secondary"
        alignButtons="center"
        contentAlign="center"
      >
        {dialogMessage}
      </DialogBox>
    );
  }

  let formTitle = '';

  // set title if there is a title
  if (title) {
    formTitle = title;
  } else if (type === 'copy') {
    formTitle = `Copy ${listItemName}`; // case when copying an item
  } else if (itemId) {
    formTitle = `Edit ${listItemName}`; // case when editing a item
  } else {
    formTitle = `Add a new ${listItemName}`; // case when adding a new item
  }

  let heading = (
    <Typography variant="h5" className={styles.Title}>
      <IconButton disabled className={styles.Icon}>
        {icon}
      </IconButton>
      {formTitle}
    </Typography>
  );

  if (advanceSearch) {
    const data = advanceSearch({});
    if (data && data.heading) heading = data.heading;
  }

  const backLink = backLinkButton ? (
    <div className={styles.BackLink}>
      <Link to={backLinkButton.link}>
        <BackIcon />
        {backLinkButton.text}
      </Link>
    </div>
  ) : null;

  return (
    <div className={styles.ItemAdd}>
      {dialogBox}
      {heading}
      {backLink}
      {form}
    </div>
  );
}
Example #23
Source File: extractBeanValidationErrors.test.ts    From jmix-frontend with Apache License 2.0 4 votes vote down vote up
describe('extractBeanValidationErrors()', () => {
  it('returns fieldErrors when there are field-related constraint violations', () => {
    const apolloError: ApolloError = {
      ...apolloErrorPartial,
      graphQLErrors: [
        {
          ...graphQLErrorPartial,
          extensions: {
            constraintViolations: [
              {
                path: 'regNumber',
                message: 'error message',
              },
              {
                path: 'manufacturer',
                message: 'another message',
              }
            ]
          }
        }
      ],
      clientErrors: []
    };
    const errors = extractBeanValidationErrors(apolloError);
    expect(errors?.fieldErrors?.size).toEqual(2);
    expect(errors?.fieldErrors?.get('regNumber')).toEqual(['error message']);
    expect(errors?.fieldErrors?.get('manufacturer')).toEqual(['another message']);
  });

  it('returns globalErrors when there are non-field-related constraint violations', () => {
    const apolloError: ApolloError = {
      ...apolloErrorPartial,
      graphQLErrors: [
        {
          ...graphQLErrorPartial,
          extensions: {
            constraintViolations: [
              {
                path: '',
                message: 'global error'
              }
            ]
          }
        }
      ],
      clientErrors: []
    };

    const errors = extractBeanValidationErrors(apolloError);
    expect(errors?.globalErrors).toEqual(['global error']);
  });

  it('returns both fieldErrors and globalErrors when there are both types of constraint violations', () => {
    const apolloError: ApolloError = {
      ...apolloErrorPartial,
      graphQLErrors: [
        {
          ...graphQLErrorPartial,
          extensions: {
            constraintViolations: [
              {
                path: 'regNumber',
                message: 'error message',
              },
              {
                path: 'manufacturer',
                message: 'another message',
              },
              {
                path: '',
                message: 'global error'
              }
            ]
          }
        }
      ],
      clientErrors: []
    };

    const errors = extractBeanValidationErrors(apolloError);
    expect(errors?.globalErrors).toEqual(['global error']);
    expect(errors?.fieldErrors?.size).toEqual(2);
    expect(errors?.fieldErrors?.get('regNumber')).toEqual(['error message']);
    expect(errors?.fieldErrors?.get('manufacturer')).toEqual(['another message']);
  });

  it('multiple errors on same field', () => {
    const apolloError: ApolloError = {
      ...apolloErrorPartial,
      graphQLErrors: [
        {
          ...graphQLErrorPartial,
          extensions: {
            constraintViolations: [
              {
                path: 'regNumber',
                message: 'error1',
              },
              {
                path: 'regNumber',
                message: 'error2',
              },
              {
                path: 'regNumber',
                message: 'error3'
              }
            ]
          }
        }
      ],
      clientErrors: []
    };

    const errors = extractBeanValidationErrors(apolloError);
    expect(errors?.fieldErrors?.size).toEqual(1);
    expect(errors?.fieldErrors?.get('regNumber')).toEqual(['error1', 'error2', 'error3']);
  });

  it('returns undefined when there are no constraint violations', () => {
    const apolloError: ApolloError = {
      ...apolloErrorPartial,
      graphQLErrors: [
        {
          ...graphQLErrorPartial,
          extensions: {}
        }
      ],
      clientErrors: []
    };

    expect(extractBeanValidationErrors(apolloError)).toBeUndefined();
    expect(extractBeanValidationErrors(undefined)).toBeUndefined();
  });
});
Example #24
Source File: generate.hooks.ts    From condo with MIT License 4 votes vote down vote up
export function generateReactHooks<GQL, GQLInput, UIForm, UI, Q> (gql, { convertToGQLInput, convertToUIState }: IHookConverters<GQL, GQLInput, UI, UIForm>): IHookResult<UI, UIForm, Q> {

    function useObject (variables: Q, options?: QueryHookOptions<{ objs?: GQL[], meta?: { count?: number } }, Q>) {
        const { loading, refetch, fetchMore, objs, count, error } = useObjects(variables, options)
        if (count && count > 1) throw new Error('Wrong query condition! return more then one result')
        const obj = (objs.length) ? objs[0] : null
        return { loading, refetch, fetchMore, obj, error }
    }
    function useObjects (variables: Q, options?: QueryHookOptions<{ objs?: GQL[], meta?: { count?: number } }, Q>) {
        const intl = useIntl()
        const ServerErrorPleaseTryAgainLaterMsg = intl.formatMessage({ id: 'ServerErrorPleaseTryAgainLater' })
        const AccessErrorMsg = intl.formatMessage({ id: 'AccessError' })

        const result = useQuery<{ objs?: GQL[], meta?: { count?: number } }, Q>(gql.GET_ALL_OBJS_WITH_COUNT_QUERY, {
            variables,
            notifyOnNetworkStatusChange: true,
            ...options,
        })

        let error: ApolloError | string
        if (error && String(error).includes('not have access')) {
            error = AccessErrorMsg
        } else if (error) {
            error = ServerErrorPleaseTryAgainLaterMsg
        }

        const objs: UI[] = getObjects(result.data, convertToUIState)

        const count = (result.data && result.data.meta) ? result.data.meta.count : null

        return {
            loading: result.loading,
            refetch: result.refetch,
            fetchMore: result.fetchMore,
            objs,
            count,
            error,
        }
    }

    /**
     * Client hook that uses create-mutation of current schema
     * @param attrs - values, that will be passed to update input unchanged by form
     */
    function useCreate (attrs: UIForm | Record<string, unknown> = {}, onComplete) {
        if (typeof attrs !== 'object' || !attrs) throw new Error('useCreate(): invalid attrs argument')
        const [rowAction] = useMutation(gql.CREATE_OBJ_MUTATION)
        async function _action (state: UIForm) {
            const { data, errors } = await rowAction({
                variables: { data: convertToGQLInput({ ...state, ...attrs }) },
            })
            if (data && data.obj) {
                const result = convertToUIState(data.obj)
                if (onComplete) onComplete(result)
                return result
            }
            if (errors) {
                console.warn(errors)
                throw errors
            }
            throw new Error('unknown action result')
        }

        return useMemo(() => _action, [rowAction])
    }

    function useUpdate (attrs = {}, onComplete) {
        if (typeof attrs !== 'object' || !attrs) throw new Error('useUpdate(): invalid attrs argument')
        const [rowAction] = useMutation(gql.UPDATE_OBJ_MUTATION)

        async function _action (state, obj) {
            if (!obj || !obj.id) throw new Error('No obj.id argument')
            const { data, errors } = await rowAction({
                variables: {
                    id: obj.id,
                    data: convertToGQLInput({ ...state, ...attrs }, obj),
                },
            })
            if (data && data.obj) {
                const result = convertToUIState(data.obj)
                if (onComplete) onComplete(result)
                return result
            }
            if (errors) {
                console.warn(errors)
                throw errors
            }
            throw new Error('unknown action result')
        }

        return useMemo(() => _action, [rowAction])
    }

    function useDelete (attrs = {}, onComplete) {
        if (typeof attrs !== 'object' || !attrs) throw new Error('useDelete(): invalid attrs argument')
        const [rowAction] = useMutation(gql.DELETE_OBJ_MUTATION)

        async function _action (obj) {
            if (!obj || !obj.id) throw new Error('No obj.id argument')
            const { data, errors } = await rowAction({
                variables: {
                    id: obj.id,
                },
            })
            if (data && data.obj) {
                const result = convertToUIState(data.obj)
                if (onComplete) onComplete(result)
                return result
            }
            if (errors) {
                console.warn(errors)
                throw errors
            }
            throw new Error('unknown action result')
        }

        return useMemo(() => _action, [rowAction])
    }

    function useSoftDelete (attrs = {}, onComplete) {
        if (typeof attrs !== 'object' || !attrs) throw new Error('useSoftDelete(): invalid attrs argument')
        const [rowAction] = useMutation(gql.UPDATE_OBJ_MUTATION)

        async function _action (state, obj) {
            if (!obj.id) throw new Error('No obj.id argument')
            const { data, errors } = await rowAction({
                variables: {
                    id: obj.id,
                    data: convertToGQLInput({ ...state, deletedAt: 'true' }, obj),
                },
            })
            if (data && data.obj) {
                const result = convertToUIState(data.obj)
                if (onComplete) onComplete(result)
                return result
            }
            if (errors) {
                console.warn(errors)
                throw errors
            }
            throw new Error('unknown action result')
        }

        return useMemo(() => _action, [rowAction])
    }
    return {
        gql,
        useObject,
        useObjects,
        useCreate,
        useUpdate,
        useDelete,
        useSoftDelete,
    }
}
Example #25
Source File: generate.hooks.ts    From condo with MIT License 4 votes vote down vote up
export function generateReactHooks<GQL, GQLInput, UIForm, UI, Q> (gql, { convertToGQLInput, convertToUIState }: IHookConverters<GQL, GQLInput, UI, UIForm>): IHookResult<UI, UIForm, Q> {
    function useObject (variables: Q, memoize = true) {
        const { loading, refetch, objs, count, error } = useObjects(variables, memoize)
        if (count && count > 1) throw new Error('Wrong query condition! return more then one result')
        const obj = (objs.length) ? objs[0] : null
        return { loading, refetch, obj, error }
    }

    function useObjects (variables: Q, memoize = true) {
        const intl = useIntl()
        const ServerErrorPleaseTryAgainLaterMsg = intl.formatMessage({ id: 'ServerErrorPleaseTryAgainLater' })
        const AccessErrorMsg = intl.formatMessage({ id: 'AccessError' })

        // eslint-disable-next-line prefer-const
        const result = useQuery<{ objs?: GQL[], meta?: { count?: number } }, Q>(gql.GET_ALL_OBJS_WITH_COUNT_QUERY, {
            variables,
        })

        let error: ApolloError | string
        if (error && String(error).includes('not have access')) {
            error = AccessErrorMsg
        } else if (error) {
            error = ServerErrorPleaseTryAgainLaterMsg
        }

        /*
        * There is bug here with nested objs memoization.
        *
        * We should use this tricky solution for manually control default memoization flow.
        * React and eslint recommend to avoid using reactHooks in conditional statements,
        * as result, we should do some tricks with initial objs value calculation.
        * TODO: debug and remove useMemo later
        */
        let objs: UI[] = useMemo(() => {
            return getObjects(result.data, convertToUIState)
        }, [result.data])

        if (!memoize) {
            objs = getObjects(result.data, convertToUIState)
        }

        const count = (result.data && result.data.meta) ? result.data.meta.count : null

        return {
            loading: result.loading,
            refetch: result.refetch,
            fetchMore: result.fetchMore,
            objs,
            count,
            error,
        }
    }

    function useCreate (attrs: UIForm | Record<string, unknown> = {}, onComplete) {
        if (typeof attrs !== 'object' || !attrs) throw new Error('useCreate(): invalid attrs argument')
        const [rowAction] = useMutation(gql.CREATE_OBJ_MUTATION)

        async function _action (state: UIForm) {
            const { data, errors } = await rowAction({
                variables: { data: convertToGQLInput({ ...state, ...attrs }) },
            })
            if (data && data.obj) {
                const result = convertToUIState(data.obj)
                if (onComplete) onComplete(result)
                return result
            }
            if (errors) {
                console.warn(errors)
                throw errors
            }
            throw new Error('unknown action result')
        }

        return useMemo(() => _action, [rowAction])
    }

    function useUpdate (attrs = {}, onComplete) {
        if (typeof attrs !== 'object' || !attrs) throw new Error('useUpdate(): invalid attrs argument')
        const [rowAction] = useMutation(gql.UPDATE_OBJ_MUTATION)

        async function _action (state, obj) {
            if (!obj || !obj.id) throw new Error('No obj.id argument')
            const { data, errors } = await rowAction({
                variables: {
                    id: obj.id,
                    data: convertToGQLInput({ ...state, ...attrs }, obj),
                },
            })
            if (data && data.obj) {
                const result = convertToUIState(data.obj)
                if (onComplete) onComplete(result)
                return result
            }
            if (errors) {
                console.warn(errors)
                throw errors
            }
            throw new Error('unknown action result')
        }

        return useMemo(() => _action, [rowAction])
    }

    function useDelete (attrs = {}, onComplete) {
        if (typeof attrs !== 'object' || !attrs) throw new Error('useDelete(): invalid attrs argument')
        const [rowAction] = useMutation(gql.DELETE_OBJ_MUTATION)

        async function _action (obj) {
            if (!obj || !obj.id) throw new Error('No obj.id argument')
            const { data, errors } = await rowAction({
                variables: {
                    id: obj.id,
                },
            })
            if (data && data.obj) {
                const result = convertToUIState(data.obj)
                if (onComplete) onComplete(result)
                return result
            }
            if (errors) {
                console.warn(errors)
                throw errors
            }
            throw new Error('unknown action result')
        }

        return useMemo(() => _action, [rowAction])
    }

    return {
        gql,
        useObject,
        useObjects,
        useCreate,
        useUpdate,
        useDelete,
    }
}