Skip to content

Commit 1bbb5fc

Browse files
authored
Merge pull request #12 from oslabs-beta/sh/jest-ts-config
Jest and TS Config with initial Token Bucket tests
2 parents 2f838b8 + f6241df commit 1bbb5fc

File tree

9 files changed

+601
-102
lines changed

9 files changed

+601
-102
lines changed

.eslintrc.json

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,13 @@
1515
"parser": "@typescript-eslint/parser",
1616
"parserOptions": {
1717
"ecmaVersion": "latest",
18-
"project": "./tsconfig.eslint.json"
18+
"project": "./tsconfig.json"
1919
},
20-
"plugins": ["import"],
20+
"plugins": ["import", "prettier"],
2121
"rules": {
22-
"indent": [
23-
"warn",
24-
4,
25-
{
26-
"SwitchCase": 2
27-
}
22+
23+
"prettier/prettier": [
24+
"error"
2825
]
2926
},
3027
"ignorePatterns": ["jest.config.js"]

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
# graph-beaver
1+
# GraphQL-Gate
22

33
A GraphQL rate limiting library using query complexity analysis.

package-lock.json

Lines changed: 268 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
2-
"name": "graphql-complexity-limit",
2+
"name": "graphql-gate",
33
"version": "1.0.0",
44
"description": "A GraphQL rate limiting library using query complexity analysis.",
55
"main": "index.js",
66
"scripts": {
77
"test": "jest --passWithNoTests",
8-
"lint": "eslint src",
9-
"lint:fix": "eslint --fix src",
8+
"lint": "eslint src test",
9+
"lint:fix": "eslint --fix src test",
1010
"prettier": "prettier --write .",
1111
"prepare": "husky install"
1212
},
@@ -26,6 +26,7 @@
2626
"@babel/preset-env": "^7.17.12",
2727
"@babel/preset-typescript": "^7.17.12",
2828
"@types/jest": "^27.5.1",
29+
"@types/redis-mock": "^0.17.1",
2930
"@typescript-eslint/eslint-plugin": "^5.24.0",
3031
"@typescript-eslint/parser": "^5.24.0",
3132
"babel-jest": "^28.1.0",
@@ -34,10 +35,12 @@
3435
"eslint-config-airbnb-typescript": "^17.0.0",
3536
"eslint-config-prettier": "^8.5.0",
3637
"eslint-plugin-import": "^2.26.0",
38+
"eslint-plugin-prettier": "^4.0.0",
3739
"husky": "^8.0.1",
3840
"jest": "^28.1.0",
3941
"lint-staged": "^12.4.1",
4042
"prettier": "2.6.2",
43+
"redis-mock": "^0.56.3",
4144
"ts-jest": "^28.0.2",
4245
"typescript": "^4.6.4"
4346
},
@@ -46,6 +49,7 @@
4649
"*.{js,ts,css,md}": "prettier --write --ignore-unknown"
4750
},
4851
"dependencies": {
52+
"redis": "^4.1.0",
4953
"graphql": "^16.5.0"
5054
}
5155
}

src/@types/rateLimit.d.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,23 @@ interface RateLimiter {
22
/**
33
* Checks if a request is allowed under the given conditions and withdraws the specified number of tokens
44
* @param uuid Unique identifier for the user associated with the request
5+
* @param timestamp UNIX format timestamp of when request was received
56
* @param tokens Number of tokens being used in this request. Optional
6-
* @returns true if the request is allowed
7+
* @returns a RateLimiterResponse indicating with a sucess and tokens property indicating the number of tokens remaining
78
*/
8-
processRequest: (uuid: string, tokens?: number) => boolean;
9-
/**
10-
* Connects the RateLimiter instance to a db to cache current token usage for connected users
11-
* @param uri database connection string
12-
*/
13-
connect: (uri: string) => void;
9+
processRequest: (
10+
uuid: string,
11+
timestamp: number,
12+
tokens?: number
13+
) => Promise<RateLimiterResponse>;
14+
}
15+
16+
interface RateLimiterResponse {
17+
success: boolean;
18+
tokens?: number;
19+
}
20+
21+
interface RedisBucket {
22+
tokens: number;
23+
timestamp: number;
1424
}

src/rateLimiters/tokenBucket.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,47 @@
1+
import { RedisClientType } from 'redis';
2+
13
/**
2-
*
4+
* The TokenBucket instance of a RateLimiter limits requests based on a unique user ID.
5+
* Whenever a user makes a request the following steps are performed:
6+
* 1. Refill the bucket based on time elapsed since the previous request
7+
* 2. Update the timestamp of the last request.
8+
* 3. Allow the request and remove the requested amount of tokens from the bucket if the user has enough.
9+
* 4. Otherwise, disallow the request and do not update the token total.
310
*/
411
class TokenBucket implements RateLimiter {
512
capacity: number;
613

714
refillRate: number;
815

16+
client: RedisClientType;
17+
918
/**
1019
* Create a new instance of a TokenBucket rate limiter that can be connected to any database store
1120
* @param capacity max token bucket capacity
1221
* @param refillRate rate at which the token bucket is refilled
22+
* @param client redis client where rate limiter will cache information
1323
*/
14-
constructor(capacity: number, refillRate: number) {
24+
constructor(capacity: number, refillRate: number, client: RedisClientType) {
1525
this.capacity = capacity;
1626
this.refillRate = refillRate;
27+
this.client = client;
28+
if (refillRate <= 0 || capacity <= 0)
29+
throw Error('TokenBucket refillRate and capacity must be positive');
1730
}
1831

19-
processRequest(uuid: string, tokens?: number): boolean {
32+
async processRequest(
33+
uuid: string,
34+
timestamp: number,
35+
tokens = 1
36+
): Promise<RateLimiterResponse> {
2037
throw Error(`TokenBucket.processRequest not implemented, ${this}`);
2138
}
2239

23-
connect(uri: string) {
24-
throw Error(`TokenBucket.connect not implemented, ${this}`);
25-
}
26-
2740
/**
28-
* @returns current size of the token bucket.
41+
* Resets the rate limiter to the intial state by clearing the redis store.
2942
*/
30-
getSize(uuid: string): number {
31-
throw Error(`TokenBucket.connect not implemented, ${this}`);
43+
reset(): void {
44+
throw Error(`TokenBucket.reset not implemented, ${this}`);
3245
}
3346
}
3447

0 commit comments

Comments
 (0)