Skip to content

Commit e8892b1

Browse files
jnsdlsjoaquim-vergesgithub-actions[bot]
authored
Pass team instead of project to rate limit (#6101)
Co-authored-by: Joaquim Verges <joaquim.verges@gmail.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 073b25a commit e8892b1

File tree

8 files changed

+28
-63
lines changed

8 files changed

+28
-63
lines changed

.changeset/large-beds-move.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@thirdweb-dev/service-utils": minor
3+
---
4+
5+
pass `team` instead of `project` to `rateLimit`

packages/service-utils/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @thirdweb-dev/service-utils
22

3+
## 0.7.3
4+
5+
### Patch Changes
6+
7+
- [#6084](https://github.com/thirdweb-dev/js/pull/6084) [`b5e327e`](https://github.com/thirdweb-dev/js/commit/b5e327e5ec745ce1beb9a79404fa5b11d0c5588e) Thanks [@jnsdls](https://github.com/jnsdls)! - add `billingPlanVersion` to `TeamResponse`
8+
39
## 0.7.2
410

511
### Patch Changes

packages/service-utils/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@thirdweb-dev/service-utils",
3-
"version": "0.7.2",
3+
"version": "0.7.3",
44
"type": "module",
55
"main": "dist/cjs/index.js",
66
"module": "dist/esm/index.js",

packages/service-utils/src/core/api.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export type TeamResponse = {
4545
slug: string;
4646
image: string | null;
4747
billingPlan: "free" | "starter" | "growth" | "pro";
48+
billingPlanVersion: number;
4849
createdAt: Date;
4950
updatedAt: Date | null;
5051
billingEmail: string | null;
@@ -163,24 +164,3 @@ export async function fetchTeamAndProject(
163164
);
164165
}
165166
}
166-
167-
export async function updateRateLimitedAt(
168-
projectId: string,
169-
config: CoreServiceConfig,
170-
): Promise<void> {
171-
const { apiUrl, serviceScope: scope, serviceApiKey } = config;
172-
173-
const url = `${apiUrl}/usage/rateLimit`;
174-
175-
await fetch(url, {
176-
method: "PUT",
177-
headers: {
178-
"x-service-api-key": serviceApiKey,
179-
"content-type": "application/json",
180-
},
181-
body: JSON.stringify({
182-
apiKeyId: projectId, // projectId is the apiKeyId
183-
scope,
184-
}),
185-
});
186-
}

packages/service-utils/src/core/rateLimit/index.ts

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import {
2-
type CoreServiceConfig,
3-
type ProjectResponse,
4-
updateRateLimitedAt,
5-
} from "../api.js";
1+
import type { CoreServiceConfig, TeamResponse } from "../api.js";
62
import type { RateLimitResult } from "./types.js";
73

84
const RATE_LIMIT_WINDOW_SECONDS = 10;
@@ -14,7 +10,7 @@ type IRedis = {
1410
};
1511

1612
export async function rateLimit(args: {
17-
project?: ProjectResponse;
13+
team: TeamResponse;
1814
limitPerSecond: number;
1915
serviceConfig: CoreServiceConfig;
2016
redis: IRedis;
@@ -25,13 +21,7 @@ export async function rateLimit(args: {
2521
*/
2622
sampleRate?: number;
2723
}): Promise<RateLimitResult> {
28-
const {
29-
project,
30-
limitPerSecond,
31-
serviceConfig,
32-
redis,
33-
sampleRate = 1.0,
34-
} = args;
24+
const { team, limitPerSecond, serviceConfig, redis, sampleRate = 1.0 } = args;
3525

3626
const shouldSampleRequest = Math.random() < sampleRate;
3727
if (!shouldSampleRequest) {
@@ -57,7 +47,7 @@ export async function rateLimit(args: {
5747
const timestampWindow =
5848
Math.floor(Date.now() / (1000 * RATE_LIMIT_WINDOW_SECONDS)) *
5949
RATE_LIMIT_WINDOW_SECONDS;
60-
const key = `rate-limit:${serviceScope}:${project?.id}:${timestampWindow}`;
50+
const key = `rate-limit:${serviceScope}:${team.id}:${timestampWindow}`;
6151

6252
// Increment and get the current request count in this window.
6353
const requestCount = await redis.incr(key);
@@ -71,17 +61,6 @@ export async function rateLimit(args: {
7161
limitPerSecond * sampleRate * RATE_LIMIT_WINDOW_SECONDS;
7262

7363
if (requestCount > limitPerWindow) {
74-
/**
75-
* Report rate limit hits.
76-
* Only track rate limit when its hit for the first time.
77-
* Not waiting for tracking to complete as user doesn't need to wait.
78-
*/
79-
if (requestCount === limitPerWindow + 1 && project?.id) {
80-
updateRateLimitedAt(project.id, serviceConfig).catch(() => {
81-
// no-op
82-
});
83-
}
84-
8564
return {
8665
rateLimited: true,
8766
requestCount,

packages/service-utils/src/core/rateLimit/rateLimit.test.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2-
import { validProjectResponse, validServiceConfig } from "../../mocks.js";
3-
import { updateRateLimitedAt } from "../api.js";
2+
import { validServiceConfig, validTeamResponse } from "../../mocks.js";
43
import { rateLimit } from "./index.js";
54

65
const mockRedis = {
76
incr: vi.fn(),
87
expire: vi.fn(),
98
};
109

11-
// Mocking the updateRateLimitedAt function
12-
vi.mock("../../../src/core/api", () => ({
13-
updateRateLimitedAt: vi.fn().mockResolvedValue({}),
14-
}));
15-
1610
describe("rateLimit", () => {
1711
beforeEach(() => {
1812
// Clear mock function calls and reset any necessary state.
@@ -27,7 +21,7 @@ describe("rateLimit", () => {
2721

2822
it("should not rate limit if service scope is not in rate limits", async () => {
2923
const result = await rateLimit({
30-
project: validProjectResponse,
24+
team: validTeamResponse,
3125
limitPerSecond: 0,
3226
serviceConfig: validServiceConfig,
3327
redis: mockRedis,
@@ -44,7 +38,7 @@ describe("rateLimit", () => {
4438
mockRedis.incr.mockResolvedValue(50); // Current count is 50 requests in 10 seconds.
4539

4640
const result = await rateLimit({
47-
project: validProjectResponse,
41+
team: validTeamResponse,
4842
limitPerSecond: 5,
4943
serviceConfig: validServiceConfig,
5044
redis: mockRedis,
@@ -55,15 +49,15 @@ describe("rateLimit", () => {
5549
requestCount: 50,
5650
rateLimit: 50,
5751
});
58-
expect(updateRateLimitedAt).not.toHaveBeenCalled();
52+
5953
expect(mockRedis.expire).not.toHaveBeenCalled();
6054
});
6155

6256
it("should rate limit if exceeded hard limit", async () => {
6357
mockRedis.incr.mockResolvedValue(51);
6458

6559
const result = await rateLimit({
66-
project: validProjectResponse,
60+
team: validTeamResponse,
6761
limitPerSecond: 5,
6862
serviceConfig: validServiceConfig,
6963
redis: mockRedis,
@@ -77,15 +71,15 @@ describe("rateLimit", () => {
7771
errorMessage: `You've exceeded your storage rate limit at 5 reqs/sec. To get higher rate limits, contact us at https://thirdweb.com/contact-us.`,
7872
errorCode: "RATE_LIMIT_EXCEEDED",
7973
});
80-
expect(updateRateLimitedAt).toHaveBeenCalled();
74+
8175
expect(mockRedis.expire).not.toHaveBeenCalled();
8276
});
8377

8478
it("expires on the first incr request only", async () => {
8579
mockRedis.incr.mockResolvedValue(1);
8680

8781
const result = await rateLimit({
88-
project: validProjectResponse,
82+
team: validTeamResponse,
8983
limitPerSecond: 5,
9084
serviceConfig: validServiceConfig,
9185
redis: mockRedis,
@@ -104,7 +98,7 @@ describe("rateLimit", () => {
10498
vi.spyOn(global.Math, "random").mockReturnValue(0.08);
10599

106100
const result = await rateLimit({
107-
project: validProjectResponse,
101+
team: validTeamResponse,
108102
limitPerSecond: 5,
109103
serviceConfig: validServiceConfig,
110104
redis: mockRedis,
@@ -127,7 +121,7 @@ describe("rateLimit", () => {
127121
vi.spyOn(global.Math, "random").mockReturnValue(0.15);
128122

129123
const result = await rateLimit({
130-
project: validProjectResponse,
124+
team: validTeamResponse,
131125
limitPerSecond: 5,
132126
serviceConfig: validServiceConfig,
133127
redis: mockRedis,

packages/service-utils/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type {
1010
TeamResponse,
1111
} from "./core/api.js";
1212

13-
export { fetchTeamAndProject, updateRateLimitedAt } from "./core/api.js";
13+
export { fetchTeamAndProject } from "./core/api.js";
1414

1515
export {
1616
authorizeBundleId,

packages/service-utils/src/mocks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const validTeamResponse: TeamResponse = {
4242
createdAt: new Date("2024-06-01"),
4343
updatedAt: new Date("2024-06-01"),
4444
billingPlan: "free",
45+
billingPlanVersion: 1,
4546
billingEmail: "test@example.com",
4647
billingStatus: "noPayment",
4748
growthTrialEligible: false,

0 commit comments

Comments
 (0)