Skip to content

chore: more throtteling tests #396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/rate-limit-throttle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,16 @@ type ThrottleType = 'auto' | string

const PERCENTAGE_REGEX = /(?<value>\d+)(%)/

function calculateLimit(type: ThrottleType, max = 7) {
const HEADERS = {
// @desc The maximum amount of requests which can be made in a second.
RATE_LIMIT: 'x-contentful-ratelimit-second-limit',
// @desc The number of seconds until the next request can be made.
RATE_LIMIT_RESET: 'x-contentful-ratelimit-second-reset',
// @desc The remaining amount of requests which can be made until the next secondly reset.
RATE_LIMIT_REMAINING: 'x-contentful-ratelimit-second-remaining',
} as const

export function calculateLimit(type: ThrottleType, max = 7) {
let limit = max

if (PERCENTAGE_REGEX.test(type)) {
Expand Down Expand Up @@ -47,14 +56,15 @@ export default (axiosInstance: AxiosInstance, type: ThrottleType | number = 'aut

const responseInterceptorId = axiosInstance.interceptors.response.use(
(response) => {
// If we haven't yet calculated the limit based on the headers, do so now
if (
!isCalculated &&
isString(type) &&
(type === 'auto' || PERCENTAGE_REGEX.test(type)) &&
response.headers &&
response.headers['x-contentful-ratelimit-second-limit']
response.headers[HEADERS.RATE_LIMIT]
) {
const rawLimit = parseInt(response.headers['x-contentful-ratelimit-second-limit'])
const rawLimit = parseInt(response.headers[HEADERS.RATE_LIMIT])
const nextLimit = calculateLimit(type, rawLimit)

if (nextLimit !== limit) {
Expand Down
2 changes: 1 addition & 1 deletion src/rate-limit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default function rateLimit(instance: AxiosInstance, maxRetry = 5): void {
} else if (response.status === 429) {
// 429 errors are exceeded rate limit exceptions
retryErrorType = 'Rate limit'
// all headers are lowercased by axios https://github.com/mzabriskie/axios/issues/413
// all headers are lower-cased by axios https://github.com/mzabriskie/axios/issues/413
if (response.headers && error.response.headers['x-contentful-ratelimit-reset']) {
wait = response.headers['x-contentful-ratelimit-reset']
}
Expand Down
17 changes: 17 additions & 0 deletions test/unit/rate-limit-throttle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'
import { AxiosInstance } from '../../src'
import createHttpClient from '../../src/create-http-client'
import { calculateLimit } from '../../src/rate-limit-throttle'

const logHandlerStub = vi.fn()

Expand Down Expand Up @@ -141,3 +142,19 @@ describe('throttle to rate limit axios interceptor', () => {
},
)
})

describe('a calculate limit function', () => {
describe('with type "auto"', () => {
it('always returns the given max limit', () => {
expect(calculateLimit('auto', 10)).toEqual(10)
expect(calculateLimit('auto', 1)).toEqual(1)
})
})
describe('with %', () => {
it('always returns % of max limit', () => {
expect(calculateLimit('0%', 10)).toEqual(1)
expect(calculateLimit('50%', 10)).toEqual(5)
expect(calculateLimit('100%', 10)).toEqual(10)
})
})
})