Skip to content

Commit

Permalink
Merge pull request #377 from pagarme/feat/BPROC-454-change-timeout-time
Browse files Browse the repository at this point in the history
[BPROC-454] Change Timeout
  • Loading branch information
VictorSamp authored Jun 22, 2022
2 parents b6d157b + 1816951 commit 002c039
Show file tree
Hide file tree
Showing 10 changed files with 240 additions and 20 deletions.
5 changes: 5 additions & 0 deletions src/lib/helpers/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ function isA4XXError (error) {
path(['response', 'status'], error) < 500)
}

function isTimeoutError (error) {
return (path(['code'], error)) === 'ECONNABORTED'
}

function handleError (err) {
if (err.name === 'SequelizeUniqueConstraintError') {
return buildFailureResponse(400, err)
Expand All @@ -37,4 +41,5 @@ function handleError (err) {
module.exports = {
isA4XXError,
handleError,
isTimeoutError,
}
10 changes: 10 additions & 0 deletions src/lib/helpers/providers.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,17 @@ const changeIssuerWhenInterestOrFine = (boleto, operationId) => {
return boleto
}

const buildBoletoApiErrorResponse = ({ code, message }) => ({
data: {
errors: [{
code,
message,
}],
},
})

module.exports = {
changeIssuerWhenInterestOrFine,
isEmptyOrNull,
buildBoletoApiErrorResponse,
}
5 changes: 5 additions & 0 deletions src/lib/http/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ const parse = curryN(2, (
return reject(new ValidationError({ errors }))
}))

const getRequestTimeoutMs = (timeoutMs = 10000, timeoutEnvTest = 25000) => (
process.env.APP_ENV === 'prd' ? timeoutMs : timeoutEnvTest
)

module.exports = {
parse,
getRequestTimeoutMs,
}
22 changes: 16 additions & 6 deletions src/providers/boleto-api-bradesco-shopfacil/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ const config = require('../../config/providers')
const responseCodeMap = require('./response-codes')
const { encodeBase64 } = require('../../lib/encoding')
const { makeFromLogger } = require('../../lib/logger')
const { isA4XXError } = require('../../lib/helpers/errors')
const { getRequestTimeoutMs } = require('../../lib/http/request')
const { isA4XXError, isTimeoutError } = require('../../lib/helpers/errors')
const { buildBoletoApiErrorResponse } = require('../../lib/helpers/providers')
const {
formatDate,
getDocumentType,
Expand Down Expand Up @@ -108,7 +110,7 @@ const buildPayload = (boleto, operationId) => {
}

const sendRequestToBoletoApi = async (payload, headers) => {
const timeoutMs = process.env.APP_ENV === 'prd' ? 50000 : 25000
const timeoutMs = getRequestTimeoutMs(10000)

const axiosPayload = {
data: payload,
Expand All @@ -119,10 +121,15 @@ const sendRequestToBoletoApi = async (payload, headers) => {
}

try {
const response = await axios.request(axiosPayload)

return response
return await axios.request(axiosPayload)
} catch (error) {
if (isTimeoutError(error)) {
return buildBoletoApiErrorResponse({
code: error.code,
message: `A requisição à BoletoApi excedeu o tempo limite de ${timeoutMs}ms`,
})
}

if (error && error.response && isA4XXError(error)) {
return error.response
}
Expand All @@ -147,8 +154,10 @@ const translateResponseCode = (axiosResponse) => {

const firstError = axiosResponseData.errors[0]
const responseCode = firstError.code
const isBoletoApiError = responseCode.startsWith('MP')
const isAxiosTimeoutError = responseCode === 'ECONNABORTED'

if (responseCode.substring(0, 2) === 'MP') {
if (isBoletoApiError || isAxiosTimeoutError) {
const defaultMundipaggError = {
message: firstError.message,
status: 'refused',
Expand Down Expand Up @@ -238,4 +247,5 @@ module.exports = {
buildPayload,
getProvider,
translateResponseCode,
sendRequestToBoletoApi,
}
22 changes: 16 additions & 6 deletions src/providers/boleto-api-caixa/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ const {

const { encodeBase64 } = require('../../lib/encoding')
const { makeFromLogger } = require('../../lib/logger')
const { isA4XXError } = require('../../lib/helpers/errors')
const { getRequestTimeoutMs } = require('../../lib/http/request')
const { isA4XXError, isTimeoutError } = require('../../lib/helpers/errors')
const { buildBoletoApiErrorResponse } = require('../../lib/helpers/providers')
const {
formatDate,
getDocumentType,
Expand Down Expand Up @@ -223,7 +225,7 @@ const buildPayload = (boleto, operationId) => {
}

const sendRequestToBoletoApi = async (payload, headers) => {
const timeoutMs = process.env.APP_ENV === 'prd' ? 50000 : 25000
const timeoutMs = getRequestTimeoutMs(10000)

const axiosPayload = {
data: payload,
Expand All @@ -234,10 +236,15 @@ const sendRequestToBoletoApi = async (payload, headers) => {
}

try {
const response = await axios.request(axiosPayload)

return response
return await axios.request(axiosPayload)
} catch (error) {
if (isTimeoutError(error)) {
return buildBoletoApiErrorResponse({
code: error.code,
message: `A requisição à BoletoApi excedeu o tempo limite de ${timeoutMs}ms`,
})
}

if (error && error.response && isA4XXError(error)) {
return error.response
}
Expand Down Expand Up @@ -289,8 +296,10 @@ const translateResponseCode = (axiosResponse) => {

const firstError = axiosResponseData.errors[0]
const responseCode = firstError.code
const isBoletoApiError = responseCode.startsWith('MP')
const isAxiosTimeoutError = responseCode === 'ECONNABORTED'

if (responseCode.substring(0, 2) === 'MP') {
if (isBoletoApiError || isAxiosTimeoutError) {
const defaultBoletoApiError = {
message: firstError.message,
status: 'refused',
Expand Down Expand Up @@ -378,4 +387,5 @@ module.exports = {
translateResponseCode,
isHtml,
getBoletoUrl,
sendRequestToBoletoApi,
}
3 changes: 2 additions & 1 deletion src/providers/bradesco/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const { encodeBase64 } = require('../../lib/encoding')
const { makeFromLogger } = require('../../lib/logger')
const responseCodeMap = require('./response-codes')
const brazilianStates = require('../../lib/helpers/brazilian-states')
const { getRequestTimeoutMs } = require('../../lib/http/request')

const {
api_key: apiKey,
Expand Down Expand Up @@ -181,7 +182,7 @@ const getProvider = ({ operationId } = defaultOptions) => {

const headers = buildHeaders()
const payload = buildPayload(boleto)
const timeoutMs = process.env.APP_ENV === 'prd' ? 50000 : 25000
const timeoutMs = getRequestTimeoutMs(10000)

return Promise.resolve({
headers,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import test from 'ava'
import Promise from 'bluebird'
import cuid from 'cuid'
import { assert } from '../../../../helpers/chai'
import { normalizeHandler } from '../../../../helpers/normalizer'
import { mock, mockFunction, restoreFunction } from '../../../../helpers/boleto'
import axios from 'axios'
import sinon from 'sinon'
import database from '../../../../../src/database'
import boletoHandler from '../../../../../src/resources/boleto'
import Provider from '../../../../../src/providers/boleto-api-bradesco-shopfacil'
import { assert } from '../../../../helpers/chai'
import { normalizeHandler } from '../../../../helpers/normalizer'
import { mock, mockFunction, restoreFunction, createBoleto } from '../../../../helpers/boleto'
import { createConfig } from '../../../../helpers/configuration'
import database from '../../../../../src/database'


const { Configuration } = database.models
const create = normalizeHandler(boletoHandler.create)
Expand Down Expand Up @@ -73,3 +76,73 @@ test('creates a boleto (status refused)', async (t) => {
queue_url: payload.queue_url,
})
})

test('sendRequestToBoletoApi: with a timeout error', async (t) => {
const timeoutMs = 25000
const operationId = cuid()
const boleto = await createBoleto()
const payload = Provider.buildPayload(boleto, operationId)
const headers = Provider.buildHeaders()
const expectedAxiosError = ({
code: 'ECONNABORTED',
})

const requestStub = sinon.stub(axios, 'request').rejects(expectedAxiosError)

const result = await Provider.sendRequestToBoletoApi(payload, headers)

t.deepEqual(
result,
{
data: {
errors: [{
code: 'ECONNABORTED',
message: `A requisição à BoletoApi excedeu o tempo limite de ${timeoutMs}ms`,
}],
},
}
)

requestStub.restore()
})

test('sendRequestToBoletoApi: with BadRequest error', async (t) => {
const operationId = cuid()
const boleto = await createBoleto()
const payload = Provider.buildPayload(boleto, operationId)
const headers = Provider.buildHeaders()
const expectedAxiosError = {
response: {
status: 400,
},
}

const axiosRequestStub = sinon.stub(axios, 'request').rejects(expectedAxiosError)

const result = await Provider.sendRequestToBoletoApi(payload, headers)

t.is(
result.status,
400
)

axiosRequestStub.restore()
})

test('sendRequestToBoletoApi: with a not mapped error', async (t) => {
const operationId = cuid()
const boleto = await createBoleto()
const payload = Provider.buildPayload(boleto, operationId)
const headers = Provider.buildHeaders()
const expectedAxiosError = ({
code: 'NotMappedError',
})

const requestStub = sinon.stub(axios, 'request').rejects(expectedAxiosError)
const error =
await t.throws(Provider.sendRequestToBoletoApi(payload, headers))

t.is(error.code, expectedAxiosError.code)

requestStub.restore()
})
76 changes: 75 additions & 1 deletion test/integration/boleto/create/boleto-api-caixa/refused.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import test from 'ava'
import Promise from 'bluebird'
import cuid from 'cuid'
import axios from 'axios'
import sinon from 'sinon'
import { assert } from '../../../../helpers/chai'
import { normalizeHandler } from '../../../../helpers/normalizer'
import { mock, mockFunction, restoreFunction } from '../../../../helpers/boleto'
import { mock, mockFunction, restoreFunction, createBoleto } from '../../../../helpers/boleto'
import boletoHandler from '../../../../../src/resources/boleto'
import Provider from '../../../../../src/providers/boleto-api-caixa'
import { createConfig } from '../../../../helpers/configuration'
import database from '../../../../../src/database'


const create = normalizeHandler(boletoHandler.create)
const { Configuration } = database.models
const externalId = cuid()
Expand Down Expand Up @@ -72,3 +75,74 @@ test('creates a boleto (status refused)', async (t) => {
queue_url: payload.queue_url,
})
})

test('sendRequestToBoletoApi: with a timeout error', async (t) => {
const timeoutMs = 25000
const operationId = cuid()
const boleto = await createBoleto()
const payload = Provider.buildPayload(boleto, operationId)
const headers = Provider.buildHeaders()
const expectedAxiosError = ({
code: 'ECONNABORTED',
})

const requestStub = sinon.stub(axios, 'request').rejects(expectedAxiosError)

const result = await Provider.sendRequestToBoletoApi(payload, headers)

t.deepEqual(
result,
{
data: {
errors: [{
code: 'ECONNABORTED',
message: `A requisição à BoletoApi excedeu o tempo limite de ${timeoutMs}ms`,
}],
},
}
)

requestStub.restore()
})

test('sendRequestToBoletoApi: with BadRequest error', async (t) => {
const operationId = cuid()
const boleto = await createBoleto()
const payload = Provider.buildPayload(boleto, operationId)
const headers = Provider.buildHeaders()
const expectedAxiosError = {
response: {
status: 400,
},
}

const axiosRequestStub = sinon.stub(axios, 'request').rejects(expectedAxiosError)

const result = await Provider.sendRequestToBoletoApi(payload, headers)

t.is(
result.status,
400
)

axiosRequestStub.restore()
})

test('sendRequestToBoletoApi: with a not mapped error', async (t) => {
const operationId = cuid()
const boleto = await createBoleto()
const payload = Provider.buildPayload(boleto, operationId)
const headers = Provider.buildHeaders()
const expectedAxiosError = ({
code: 'NotMappedError',
})

const requestStub = sinon.stub(axios, 'request').rejects(expectedAxiosError)
const error =
await t.throws(Provider.sendRequestToBoletoApi(payload, headers))

t.is(error.code, expectedAxiosError.code)

requestStub.restore()
})

22 changes: 21 additions & 1 deletion test/unit/lib/helper/errors.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import test from 'ava'
import {
isA4XXError,
isA4XXError, isTimeoutError,
} from '../../../../src/lib/helpers/errors'

test('isA4xxError: is not a 4xx error', async (t) => {
Expand Down Expand Up @@ -50,3 +50,23 @@ test('isA4xxError: is a 4xx error', async (t) => {

t.is(result, true)
})

test('isTimeoutError: is a axios timeout error', async (t) => {
const error = {
code: 'ECONNABORTED',
}

const result = isTimeoutError(error)

t.is(result, true)
})

test('isTimeoutError: is not a axios timeout error', async (t) => {
const error = {
code: 'IsNotAAxiosTimeoutError',
}

const result = isTimeoutError(error)

t.is(result, false)
})
Loading

0 comments on commit 002c039

Please sign in to comment.