Skip to content
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

rewrite in typescript and shared / distributed locking #39

Open
wants to merge 6 commits 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
7 changes: 7 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
root: true,
extends: ['@hokify'],
parserOptions: {
project: './tsconfig.eslint.json'
}
};
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/node_modules/
/coverage/
/yarn-error.log
/dist/
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"printWidth": 100,
"singleQuote": true,
"useTabs": true,
"trailingComma": "none",
"arrowParens": "avoid"
}
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# Fork
this is a fork of https://github.com/aishek/axios-rate-limit which has following changes in it:
* rewrite in Typescript
* allows passing in a custom store, to share the state between instances/servers.

Note: this module does not share state with other processes/servers by default. Use a redis or Memcached Store for shared states.

### Stores
this fork uses the stores avaialble from express rate limiting. Therefore easy extendable :)

- Memory Store _(default, built-in)_ - stores hits in-memory in the Node.js process. Does not share state with other servers or processes.
- [Redis Store](https://npmjs.com/package/rate-limit-redis)
- [Memcached Store](https://npmjs.org/package/rate-limit-memcached)

# axios-rate-limit

[![npm version](https://img.shields.io/npm/v/axios-rate-limit.svg?style=flat-square)](https://www.npmjs.com/package/axios-rate-limit)
Expand Down
231 changes: 116 additions & 115 deletions __tests__/index.js
Original file line number Diff line number Diff line change
@@ -1,119 +1,120 @@
var axios = require('axios')
var sinon = require('sinon')
const axios = require('axios');
const sinon = require('sinon');

var axiosRateLimit = require('../src/index')
const axiosRateLimit = require('../dist');

function delay (milliseconds) {
return new Promise(function (resolve) {
return setTimeout(resolve, milliseconds)
})
function delay(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}

it('not delay requests less than maxRequests', async function () {
var maxRequests = 5
var perMilliseconds = 1000
var totalRequests = 4
function adapter (config) { return Promise.resolve(config) }

var http = axiosRateLimit(
axios.create({ adapter: adapter }),
{ maxRPS: maxRequests }
)

var onSuccess = sinon.spy()

var requests = []
var start = Date.now()
for (var i = 0; i < totalRequests; i++) {
requests.push(http.get('/users').then(onSuccess))
}

await Promise.all(requests)
var end = Date.now()
expect(onSuccess.callCount).toEqual(totalRequests)
expect(end - start).toBeLessThan(perMilliseconds)
})

it('throws an error', async function () {
var maxRequests = 2
var perMilliseconds = 1000
function adapter () { return Promise.reject(new Error('fail')) }

var http = axiosRateLimit(
axios.create({ adapter: adapter }),
{ maxRequests: maxRequests, perMilliseconds: perMilliseconds }
)

expect.assertions(1)
try {
await http.get('/users')
} catch (error) {
expect(error.message).toEqual('fail')
}
})

it('support dynamic options', async function () {
function adapter (config) { return Promise.resolve(config) }

// check constructor options
var http = axiosRateLimit(
axios.create({ adapter: adapter }),
{ maxRequests: 2, perMilliseconds: 100 }
)
expect(http.getMaxRPS()).toEqual(20)

var onSuccess = sinon.spy()

var requests = []
var start = Date.now()
for (var i = 0; i < 3; i++) {
requests.push(http.get('/users').then(onSuccess))
}
await delay(90)
expect(onSuccess.callCount).toEqual(2)

await Promise.all(requests)
var end = Date.now()
expect(onSuccess.callCount).toEqual(3)
expect(end - start).toBeGreaterThan(100)
await delay(110)

// check setRateLimitOptions
http.setRateLimitOptions({ maxRequests: 3, perMilliseconds: 200 })
expect(http.getMaxRPS()).toEqual(15)

onSuccess = sinon.spy()
requests = []
start = Date.now()
for (var x = 0; x < 4; x++) {
requests.push(http.get('/users').then(onSuccess))
}
await delay(190)
end = Date.now()
expect(onSuccess.callCount).toEqual(3)

await Promise.all(requests)
end = Date.now()
expect(onSuccess.callCount).toEqual(4)
expect(end - start).toBeGreaterThan(200)
await delay(210)

// check setMaxRPS
http.setMaxRPS(3)
expect(http.getMaxRPS()).toEqual(3)

onSuccess = sinon.spy()
requests = []
start = Date.now()
for (var z = 0; z < 4; z++) {
requests.push(http.get('/users').then(onSuccess))
}
await delay(990)
end = Date.now()
expect(onSuccess.callCount).toEqual(3)

await Promise.all(requests)
end = Date.now()
expect(onSuccess.callCount).toEqual(4)
expect(end - start).toBeGreaterThan(1000)
})
it('not delay requests less than maxRequests', async () => {
const maxRequests = 5;
const perMilliseconds = 1000;
const totalRequests = 4;
function adapter(config) {
return Promise.resolve(config);
}

const http = axiosRateLimit(axios.create({ adapter }), { maxRPS: maxRequests });

const onSuccess = sinon.spy();

const requests = [];
const start = Date.now();
for (let i = 0; i < totalRequests; i++) {
requests.push(http.get('/users').then(onSuccess));
}

await Promise.all(requests);
const end = Date.now();
expect(onSuccess.callCount).toEqual(totalRequests);
expect(end - start).toBeLessThan(perMilliseconds);
});

it('throws an error', async () => {
const maxRequests = 2;
const perMilliseconds = 1000;
function adapter() {
return Promise.reject(new Error('fail'));
}

const http = axiosRateLimit(axios.create({ adapter }), {
maxRequests,
perMilliseconds
});

expect.assertions(1);
try {
await http.get('/users');
} catch (error) {
expect(error.message).toEqual('fail');
}
});

it('support dynamic options', async () => {
function adapter(config) {
return Promise.resolve(config);
}

// check constructor options
const http = axiosRateLimit(axios.create({ adapter }), {
maxRequests: 2,
perMilliseconds: 100
});
expect(http.getMaxRPS()).toEqual(20);

let onSuccess = sinon.spy();

let requests = [];
let start = Date.now();
for (let i = 0; i < 3; i++) {
requests.push(http.get('/users').then(onSuccess));
}
await delay(90);
expect(onSuccess.callCount).toEqual(2);

await Promise.all(requests);
let end = Date.now();
expect(onSuccess.callCount).toEqual(3);
expect(end - start).toBeGreaterThan(100);
await delay(110);

// check setRateLimitOptions
http.setRateLimitOptions({ maxRequests: 3, perMilliseconds: 200 });
expect(http.getMaxRPS()).toEqual(15);

onSuccess = sinon.spy();
requests = [];
start = Date.now();
for (let x = 0; x < 4; x++) {
requests.push(http.get('/users').then(onSuccess));
}
await delay(190);
end = Date.now();
expect(onSuccess.callCount).toEqual(3);

await Promise.all(requests);
end = Date.now();
expect(onSuccess.callCount).toEqual(4);
expect(end - start).toBeGreaterThan(200);
await delay(210);

// check setMaxRPS
http.setMaxRPS(3);
expect(http.getMaxRPS()).toEqual(3);

onSuccess = sinon.spy();
requests = [];
start = Date.now();
for (let z = 0; z < 4; z++) {
requests.push(http.get('/users').then(onSuccess));
}
await delay(990);
end = Date.now();
expect(onSuccess.callCount).toEqual(3);

await Promise.all(requests);
end = Date.now();
expect(onSuccess.callCount).toEqual(4);
expect(end - start).toBeGreaterThan(1000);
});
62 changes: 18 additions & 44 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,49 +1,39 @@
{
"name": "axios-rate-limit",
"name": "@hokify/axios-rate-limit",
"description": "Rate limit for axios.",
"version": "1.3.0",
"version": "2.0.4",
"license": "MIT",
"bugs": {
"url": "https://github.com/aishek/axios-rate-limit/issues"
"url": "https://github.com/hokify/axios-rate-limit/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/aishek/axios-rate-limit.git"
"url": "https://github.com/hokify/axios-rate-limit.git"
},
"homepage": "https://github.com/aishek/axios-rate-limit#readme",
"homepage": "https://github.com/hokify/axios-rate-limit#readme",
"devDependencies": {
"@logux/eslint-config": "^28.1.0",
"@hokify/eslint-config": "^1.1.5",
"axios": "^0.18.0",
"eslint": "^5.16.0",
"eslint-ci": "^1.0.0",
"eslint-config-standard": "^12.0.0",
"eslint-plugin-es5": "^1.3.1",
"eslint-plugin-import": "^2.17.2",
"eslint-plugin-import-helpers": "^0.1.4",
"eslint-plugin-jest": "^22.4.1",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.1.1",
"eslint-plugin-security": "^1.4.0",
"eslint-plugin-standard": "^4.0.0",
"husky": "^1.3.1",
"jest": "^24.7.1",
"lint-staged": "^8.1.5",
"sinon": "^7.3.2",
"yaspeller-ci": "^1.0.0"
"husky": "^6.0.0",
"jest": "^26.6.3",
"lint-staged": "^10.5.4",
"sinon": "^10.0.0",
"yaspeller-ci": "^1.0.2"
},
"peerDependencies": {
"axios": "*"
},
"main": "src/index.js",
"typings": "typings/index.d.ts",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
"src/index.js",
"typings/index.d.ts"
"dist"
],
"scripts": {
"prepublishOnly": "npm run build",
"build": "tsc",
"watch": "yarn jest --watch",
"test": "jest --forceExit --runInBand --coverage --detectOpenHandles && yarn lint && yarn spell",
"lint": "eslint-ci src/*.js __tests__/*.js",
"lint": "eslint --fix src/* __tests__/*.js",
"spell": "yaspeller-ci *.md"
},
"jest": {
Expand All @@ -58,23 +48,6 @@
"/node_modules/"
]
},
"eslintConfig": {
"plugins": [
"jest"
],
"extends": "@logux/eslint-config/browser",
"env": {
"jest/globals": true
},
"rules": {
"max-len": [
"error",
{
"ignoreComments": true
}
]
}
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
Expand All @@ -90,6 +63,7 @@
"limit"
],
"author": "Alexandr Borisov ([email protected])",
"contributors": ["Simon Tretter ([email protected])"],
"yaspeller": {
"lang": "en",
"dictionary": [
Expand Down
Loading