From 06a2014bc1d9be63984ba6b2a0ac22ae412fe304 Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Mon, 7 Nov 2022 22:51:30 +0100 Subject: [PATCH] feat: Enrich error responses --- .../src/apps/discord/auth/create-auth-data.ts | 32 +++++++--------- .../src/apps/flickr/auth/create-auth-data.ts | 32 +++++++--------- .../apps/salesforce/auth/create-auth-data.ts | 28 ++++++-------- .../src/apps/twitter/auth/create-auth-data.ts | 38 +++++++------------ packages/backend/src/errors/base.ts | 6 +-- .../backend/src/errors/create-auth-data.ts | 14 +++++++ packages/backend/src/errors/http-error.ts | 3 -- packages/backend/src/errors/http.ts | 12 ++++++ .../src/graphql/mutations/create-auth-data.ts | 6 +-- .../backend/src/helpers/graphql-instance.ts | 5 +-- .../backend/src/helpers/http-client/index.ts | 4 +- packages/backend/src/services/flow.ts | 4 +- .../src/components/AddAppConnection/index.tsx | 14 ++++--- 13 files changed, 97 insertions(+), 101 deletions(-) create mode 100644 packages/backend/src/errors/create-auth-data.ts delete mode 100644 packages/backend/src/errors/http-error.ts create mode 100644 packages/backend/src/errors/http.ts diff --git a/packages/backend/src/apps/discord/auth/create-auth-data.ts b/packages/backend/src/apps/discord/auth/create-auth-data.ts index c4dab8b0..25b83887 100644 --- a/packages/backend/src/apps/discord/auth/create-auth-data.ts +++ b/packages/backend/src/apps/discord/auth/create-auth-data.ts @@ -3,26 +3,20 @@ import { URLSearchParams } from 'url'; import scopes from '../common/scopes'; export default async function createAuthData($: IGlobalVariable) { - try { - const oauthRedirectUrlField = $.app.auth.fields.find( - (field: IField) => field.key == 'oAuthRedirectUrl' - ); - const callbackUrl = oauthRedirectUrlField.value as string; + const oauthRedirectUrlField = $.app.auth.fields.find( + (field: IField) => field.key == 'oAuthRedirectUrl' + ); + const callbackUrl = oauthRedirectUrlField.value as string; - const searchParams = new URLSearchParams({ - client_id: $.auth.data.consumerKey as string, - redirect_uri: callbackUrl, - response_type: 'code', - permissions: '2146958591', - scope: scopes.join(' '), - }); + const searchParams = new URLSearchParams({ + client_id: $.auth.data.consumerKey as string, + redirect_uri: callbackUrl, + response_type: 'code', + permissions: '2146958591', + scope: scopes.join(' '), + }); - const url = `${ - $.app.apiBaseUrl - }/oauth2/authorize?${searchParams.toString()}`; + const url = `${$.app.apiBaseUrl}/oauth2/authorize?${searchParams.toString()}`; - await $.auth.set({ url }); - } catch (error) { - throw new Error(`Error occured while verifying credentials: ${error}`); - } + await $.auth.set({ url }); } diff --git a/packages/backend/src/apps/flickr/auth/create-auth-data.ts b/packages/backend/src/apps/flickr/auth/create-auth-data.ts index 01ca50dd..383363bd 100644 --- a/packages/backend/src/apps/flickr/auth/create-auth-data.ts +++ b/packages/backend/src/apps/flickr/auth/create-auth-data.ts @@ -2,26 +2,20 @@ import { IField, IGlobalVariable } from '@automatisch/types'; import { URLSearchParams } from 'url'; export default async function createAuthData($: IGlobalVariable) { - try { - const oauthRedirectUrlField = $.app.auth.fields.find( - (field: IField) => field.key == 'oAuthRedirectUrl' - ); + const oauthRedirectUrlField = $.app.auth.fields.find( + (field: IField) => field.key == 'oAuthRedirectUrl' + ); - const callbackUrl = oauthRedirectUrlField.value; - const requestPath = '/oauth/request_token'; - const data = { oauth_callback: callbackUrl }; + const callbackUrl = oauthRedirectUrlField.value; + const requestPath = '/oauth/request_token'; + const data = { oauth_callback: callbackUrl }; - const response = await $.http.post(requestPath, data); - const responseData = Object.fromEntries(new URLSearchParams(response.data)); + const response = await $.http.post(requestPath, data); + const responseData = Object.fromEntries(new URLSearchParams(response.data)); - await $.auth.set({ - url: `${$.app.apiBaseUrl}/oauth/authorize?oauth_token=${responseData.oauth_token}&perms=delete`, - accessToken: responseData.oauth_token, - accessSecret: responseData.oauth_token_secret, - }); - } catch (error) { - throw new Error( - `Error occured while verifying credentials: ${error.message}` - ); - } + await $.auth.set({ + url: `${$.app.apiBaseUrl}/oauth/authorize?oauth_token=${responseData.oauth_token}&perms=delete`, + accessToken: responseData.oauth_token, + accessSecret: responseData.oauth_token_secret, + }); } diff --git a/packages/backend/src/apps/salesforce/auth/create-auth-data.ts b/packages/backend/src/apps/salesforce/auth/create-auth-data.ts index de43a3b8..b38f422e 100644 --- a/packages/backend/src/apps/salesforce/auth/create-auth-data.ts +++ b/packages/backend/src/apps/salesforce/auth/create-auth-data.ts @@ -2,21 +2,17 @@ import { IField, IGlobalVariable } from '@automatisch/types'; import qs from 'qs'; export default async function createAuthData($: IGlobalVariable) { - try { - const oauthRedirectUrlField = $.app.auth.fields.find( - (field: IField) => field.key == 'oAuthRedirectUrl' - ); - const redirectUri = oauthRedirectUrlField.value; - const searchParams = qs.stringify({ - client_id: $.auth.data.consumerKey as string, - redirect_uri: redirectUri, - response_type: 'code', - }); + const oauthRedirectUrlField = $.app.auth.fields.find( + (field: IField) => field.key == 'oAuthRedirectUrl' + ); + const redirectUri = oauthRedirectUrlField.value; + const searchParams = qs.stringify({ + client_id: $.auth.data.consumerKey as string, + redirect_uri: redirectUri, + response_type: 'code', + }); - await $.auth.set({ - url: `${$.auth.data.oauth2Url}/authorize?${searchParams}`, - }); - } catch (error) { - throw new Error(`Error occured while verifying credentials: ${error}`); - } + await $.auth.set({ + url: `${$.auth.data.oauth2Url}/authorize?${searchParams}`, + }); } diff --git a/packages/backend/src/apps/twitter/auth/create-auth-data.ts b/packages/backend/src/apps/twitter/auth/create-auth-data.ts index effbd8a6..d7ea1571 100644 --- a/packages/backend/src/apps/twitter/auth/create-auth-data.ts +++ b/packages/backend/src/apps/twitter/auth/create-auth-data.ts @@ -1,31 +1,21 @@ -import { IJSONObject, IField, IGlobalVariable } from '@automatisch/types'; +import { IField, IGlobalVariable } from '@automatisch/types'; import { URLSearchParams } from 'url'; export default async function createAuthData($: IGlobalVariable) { - try { - const oauthRedirectUrlField = $.app.auth.fields.find( - (field: IField) => field.key == 'oAuthRedirectUrl' - ); + const oauthRedirectUrlField = $.app.auth.fields.find( + (field: IField) => field.key == 'oAuthRedirectUrl' + ); - const callbackUrl = oauthRedirectUrlField.value; - const requestPath = '/oauth/request_token'; - const data = { oauth_callback: callbackUrl }; + const callbackUrl = oauthRedirectUrlField.value; + const requestPath = '/oauth/request_token'; + const data = { oauth_callback: callbackUrl }; - const response = await $.http.post(requestPath, data); - const responseData = Object.fromEntries(new URLSearchParams(response.data)); + const response = await $.http.post(requestPath, data); + const responseData = Object.fromEntries(new URLSearchParams(response.data)); - await $.auth.set({ - url: `${$.app.baseUrl}/oauth/authorize?oauth_token=${responseData.oauth_token}`, - accessToken: responseData.oauth_token, - accessSecret: responseData.oauth_token_secret, - }); - } catch (error) { - const errorMessages = error.response.data.errors - .map((error: IJSONObject) => error.message) - .join(' '); - - throw new Error( - `Error occured while verifying credentials: ${errorMessages}` - ); - } + await $.auth.set({ + url: `${$.app.baseUrl}/oauth/authorize?oauth_token=${responseData.oauth_token}`, + accessToken: responseData.oauth_token, + accessSecret: responseData.oauth_token_secret, + }); } diff --git a/packages/backend/src/errors/base.ts b/packages/backend/src/errors/base.ts index dedaa790..2e3e9863 100644 --- a/packages/backend/src/errors/base.ts +++ b/packages/backend/src/errors/base.ts @@ -1,14 +1,14 @@ import { IJSONObject } from '@automatisch/types'; export default class BaseError extends Error { - error = {}; + details = {}; constructor(error?: string | IJSONObject) { let computedError: Record; try { computedError = JSON.parse(error as string); } catch { - computedError = typeof error === 'string' ? { error: computedError } : error; + computedError = typeof error === 'string' ? { error } : error; } let computedMessage: string; @@ -26,7 +26,7 @@ export default class BaseError extends Error { super(computedMessage); - this.error = computedError; + this.details = computedError; this.name = this.constructor.name; } } diff --git a/packages/backend/src/errors/create-auth-data.ts b/packages/backend/src/errors/create-auth-data.ts new file mode 100644 index 00000000..871c44eb --- /dev/null +++ b/packages/backend/src/errors/create-auth-data.ts @@ -0,0 +1,14 @@ +import { IJSONObject } from '@automatisch/types'; +import BaseError from './base'; + +export default class CreateAuthDataError extends BaseError { + constructor(error: IJSONObject) { + const computedError = + ((error.response as IJSONObject)?.data as IJSONObject) || + (error.message as string); + + super(computedError); + + this.message = `Error occured while creating authorization URL!`; + } +} diff --git a/packages/backend/src/errors/http-error.ts b/packages/backend/src/errors/http-error.ts deleted file mode 100644 index 2e71ebeb..00000000 --- a/packages/backend/src/errors/http-error.ts +++ /dev/null @@ -1,3 +0,0 @@ -import BaseError from './base'; - -export default class HttpError extends BaseError {} diff --git a/packages/backend/src/errors/http.ts b/packages/backend/src/errors/http.ts new file mode 100644 index 00000000..d4396f50 --- /dev/null +++ b/packages/backend/src/errors/http.ts @@ -0,0 +1,12 @@ +import { IJSONObject } from '@automatisch/types'; +import BaseError from './base'; + +export default class HttpError extends BaseError { + constructor(error: IJSONObject) { + const computedError = + ((error.response as IJSONObject)?.data as IJSONObject) || + (error.message as string); + + super(computedError); + } +} diff --git a/packages/backend/src/graphql/mutations/create-auth-data.ts b/packages/backend/src/graphql/mutations/create-auth-data.ts index fd3e020b..ecf5737e 100644 --- a/packages/backend/src/graphql/mutations/create-auth-data.ts +++ b/packages/backend/src/graphql/mutations/create-auth-data.ts @@ -2,6 +2,7 @@ import Context from '../../types/express/context'; import axios from 'axios'; import globalVariable from '../../helpers/global-variable'; import App from '../../models/app'; +import CreateAuthDataError from '../../errors/create-auth-data'; type Params = { input: { @@ -30,12 +31,11 @@ const createAuthData = async ( const app = await App.findOneByKey(connection.key); const $ = await globalVariable({ connection, app }); - await authInstance.createAuthData($); - try { + await authInstance.createAuthData($); await axios.get(connection.formattedData.url as string); } catch (error) { - throw new Error('Error occured while creating authorization URL!'); + throw new CreateAuthDataError(error); } return connection.formattedData; diff --git a/packages/backend/src/helpers/graphql-instance.ts b/packages/backend/src/helpers/graphql-instance.ts index aa55fe71..d65fd83c 100644 --- a/packages/backend/src/helpers/graphql-instance.ts +++ b/packages/backend/src/helpers/graphql-instance.ts @@ -23,10 +23,7 @@ const graphQLInstance = graphqlHTTP({ customFormatErrorFn: (error) => { logger.error(error.path + ' : ' + error.message + '\n' + error.stack); - return { - message: error.message, - locations: error.locations, - }; + return error.originalError; }, }); diff --git a/packages/backend/src/helpers/http-client/index.ts b/packages/backend/src/helpers/http-client/index.ts index 611d5d9b..893996d9 100644 --- a/packages/backend/src/helpers/http-client/index.ts +++ b/packages/backend/src/helpers/http-client/index.ts @@ -2,7 +2,7 @@ import axios, { AxiosRequestConfig } from 'axios'; export { AxiosInstance as IHttpClient } from 'axios'; import { IHttpClientParams } from '@automatisch/types'; import { URL } from 'url'; -import HttpError from '../../errors/http-error'; +import HttpError from '../../errors/http'; const removeBaseUrlForAbsoluteUrls = ( requestConfig: AxiosRequestConfig @@ -40,7 +40,7 @@ export default function createHttpClient({ instance.interceptors.response.use( (response) => response, (error) => { - throw new HttpError(error.response.data); + throw new HttpError(error); } ); diff --git a/packages/backend/src/services/flow.ts b/packages/backend/src/services/flow.ts index 86301400..f6a256fe 100644 --- a/packages/backend/src/services/flow.ts +++ b/packages/backend/src/services/flow.ts @@ -1,7 +1,7 @@ import Flow from '../models/flow'; import globalVariable from '../helpers/global-variable'; import EarlyExitError from '../errors/early-exit'; -import HttpError from '../errors/http-error'; +import HttpError from '../errors/http'; type ProcessFlowOptions = { flowId: string; @@ -27,7 +27,7 @@ export const processFlow = async (options: ProcessFlowOptions) => { } catch (error) { if (error instanceof EarlyExitError === false) { if (error instanceof HttpError) { - $.triggerOutput.error = error.error; + $.triggerOutput.error = error.details; } else { try { $.triggerOutput.error = JSON.parse(error.message); diff --git a/packages/web/src/components/AddAppConnection/index.tsx b/packages/web/src/components/AddAppConnection/index.tsx index 627ff7a4..7b54e0fe 100644 --- a/packages/web/src/components/AddAppConnection/index.tsx +++ b/packages/web/src/components/AddAppConnection/index.tsx @@ -6,6 +6,7 @@ import DialogContentText from '@mui/material/DialogContentText'; import Dialog from '@mui/material/Dialog'; import LoadingButton from '@mui/lab/LoadingButton'; import { FieldValues, SubmitHandler } from 'react-hook-form'; +import { IJSONObject } from '@automatisch/types'; import useFormatMessage from 'hooks/useFormatMessage'; import computeAuthStepVariables from 'helpers/computeAuthStepVariables'; @@ -37,7 +38,7 @@ export default function AddAppConnection( const { application, connectionId, onClose } = props; const { name, authDocUrl, key, auth } = application; const formatMessage = useFormatMessage(); - const [errorMessage, setErrorMessage] = React.useState(null); + const [error, setError] = React.useState(null); const [inProgress, setInProgress] = React.useState(false); const hasConnection = Boolean(connectionId); const steps = hasConnection @@ -59,7 +60,7 @@ export default function AddAppConnection( if (!steps) return; setInProgress(true); - setErrorMessage(null); + setError(null); const response: Response = { key, @@ -79,9 +80,9 @@ export default function AddAppConnection( response[step.name] = stepResponse; } catch (err) { - const error = err as Error; + const error = err as IJSONObject; console.log(error); - setErrorMessage(error.message); + setError((error.graphQLErrors as IJSONObject[])?.[0]); setInProgress(false); break; @@ -116,12 +117,13 @@ export default function AddAppConnection( )} - {errorMessage && ( + {error && ( - {errorMessage} + {error.message} +
{JSON.stringify(error.details, null, 2)}
)}