import { formatApolloErrors } from 'apollo-server-errors' ; import { uniqBy } from 'ramda' ; import { LINKED } from '../../../../../constants' ; import { cancelledErrorCode , cancelledRequestStatus , } from '../../../../../errors/RequestCancelledError' ; import { LogLevel } from '../../../../logger' ; import { IOContext } from '../../typings' ; import { GraphQLServiceContext } from '../typings' ; import { createFormatError } from '../utils/error' ; import { generatePathName } from '../utils/pathname' ; const TWO_SECONDS_S = 2 ; const uniqErrorsByPath = ( errors ) => uniqBy ( e => e . extensions && e . extensions . exception && e . extensions . exception . request ? e . extensions . exception . request . path : e , errors ) ; const createLogErrorToSplunk = ( vtex ) => ( err ) => { const { route : { id } , logger , } = vtex ; try { if ( err ?. extensions ?. exception ?. code === cancelledErrorCode ) { return ; } if ( err . path ) { err . pathName = generatePathName ( err . path ) ; } const log = { ... err , routeId : id , } ; let level = err ?. extensions ?. exception ?. level as LogLevel ; if ( ! level || ! ( level === LogLevel . Error || level === LogLevel . Warn ) ) { level = LogLevel . Error ; } logger . log ( log , level ) ; } finally { if ( ! LINKED && err ?. extensions ?. exception ?. sensitive ) { delete err . extensions . exception . sensitive ; } } } ; export async function graphqlError ( ctx , next ) { const { vtex : { production } , } = ctx ; let graphQLErrors = null ; try { await next ( ) ; const response = ctx . graphql . graphqlResponse ; if ( response && Array . isArray ( response . errors ) ) { const formatter = createFormatError ( ctx ) ; graphQLErrors = formatApolloErrors ( response . errors , { formatter } ) ; } } catch ( e ) { if ( e . code === cancelledErrorCode ) { ctx . status = cancelledRequestStatus ; return ; } const formatError = createFormatError ( ctx ) ; graphQLErrors = Array . isArray ( e ) ? e . map ( formatError ) : [ formatError ( e ) ] ; ctx . status = e . statusCode || 500 ; if ( e . headers ) { ctx . set ( e . headers ) ; } } finally { if ( graphQLErrors ) { ctx . graphql . status = 'error' ; const uniqueErrors = uniqErrorsByPath ( graphQLErrors ) ; const logToSplunk = createLogErrorToSplunk ( ctx . vtex ) ; uniqueErrors . forEach ( logToSplunk ) ; if ( production ) { ctx . graphql . cacheControl . maxAge = TWO_SECONDS_S ; } else { ctx . graphql . cacheControl . noCache = true ; ctx . graphql . cacheControl . noStore = true ; } ctx . graphql . graphqlResponse = { ... ctx . graphql . graphqlResponse , errors : uniqueErrors , } ; } } }