import { CompassCatalogQueryApi, GqlError, MutationError, MutationErrorExtensions, QueryErrorExtension, SdkError, } from '@atlassian/forge-graphql-types'; import { FORGE_GRAPHQL_SDK_ERROR_SOURCE, GRAPHQL_GATEWAY_SOURCE, INTERNAL_SERVER_ERROR_TYPE, QUERY_ERROR, SDK_SCHEMA_ERROR, } from './constants'; const mapGqlErrors = (errors: Array = []): Array => errors.map((error) => ({ errorSource: error.extensions?.errorSource || FORGE_GRAPHQL_SDK_ERROR_SOURCE, errorType: error.extensions?.errorType || error.extensions?.classification || INTERNAL_SERVER_ERROR_TYPE, message: error.message, })); const traverseQueryResult = ( obj: any, path: string, defaultValue: any = null, ) => { const travel = (regexp: RegExp) => String.prototype.split .call(path, regexp) .filter(Boolean) .reduce( (res: any, key: string) => res !== null && res !== undefined ? res[key] : res, obj, ); const result = travel(/[,[\]]+?/) || travel(/[,[\].]+?/); return result === undefined || result === obj ? defaultValue : result; }; function mapQueryErrors( result: CompassCatalogQueryApi, queryErrorPaths: Array, ): Array { return queryErrorPaths.reduce((acc, errorPath) => { try { const { __typename: typename, message, extensions, } = traverseQueryResult(result, errorPath); if (typename === QUERY_ERROR) { // We place an empty object in the default so that we push an instance of the original error, even if the error doesnt have any extensions (extensions || [{}]).forEach( ({ statusCode, errorType }: QueryErrorExtension) => { acc.push({ message, statusCode, errorType, errorSource: GRAPHQL_GATEWAY_SOURCE, }); }, ); } } catch (e) { if (!(e instanceof TypeError)) { throw e; } } return acc; }, []); } function mapMutationErrors( mutationErrors: Array, ): Array { if (!mutationErrors) { return []; } const sdkErrs: Array = []; mutationErrors.forEach((mutationError) => { try { const { message } = mutationError; if (!mutationError.extensions) { sdkErrs.push({ message, errorType: INTERNAL_SERVER_ERROR_TYPE, statusCode: 500, errorSource: GRAPHQL_GATEWAY_SOURCE, }); } if (!Array.isArray(mutationError.extensions)) { const extension = mutationError.extensions as MutationErrorExtensions; sdkErrs.push({ message, errorType: extension.errorType || INTERNAL_SERVER_ERROR_TYPE, statusCode: extension.statusCode || 500, errorSource: GRAPHQL_GATEWAY_SOURCE, }); } else { // We place an empty object in the default so that we push an instance of the original error, even if the error doesnt have any extensions mutationError.extensions.forEach((ext) => { sdkErrs.push({ message, errorType: ext.errorType || INTERNAL_SERVER_ERROR_TYPE, statusCode: ext.statusCode || 500, errorSource: GRAPHQL_GATEWAY_SOURCE, }); }); } } catch (e) { if (!(e instanceof TypeError)) { throw e; } } }); return sdkErrs; } function parsingResponseError(e: Error): SdkError { if (e instanceof TypeError) { return SDK_SCHEMA_ERROR; } return { message: e.message, errorType: e.constructor.name, errorSource: FORGE_GRAPHQL_SDK_ERROR_SOURCE, statusCode: 500, }; } function parseCompoundMutationErrors( aggResp: any, variables: any, ): Array { if (!aggResp) { return []; } return Object.keys(variables) .map((mutationName) => mapMutationErrors(aggResp.compass[mutationName]?.errors), ) .flat() .filter((error: any) => error) as unknown as Array; } export { mapGqlErrors, mapQueryErrors, mapMutationErrors, parsingResponseError, parseCompoundMutationErrors, };