Skip to content

Commit

Permalink
Merge pull request #8 from willmanduffy/rate-limit-logging
Browse files Browse the repository at this point in the history
Add better logging when rate limited for Bluesky
  • Loading branch information
willmanduffy authored Dec 1, 2024
2 parents 0a41195 + 4f1b6fb commit 482ebae
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 11 deletions.
39 changes: 38 additions & 1 deletion src/api-wrappers/bluesky.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { BlueSky } from "./bluesky";
import { AtpAgent, ComAtprotoServerCreateSession } from "@atproto/api";
import { mockEnv } from "../../tests/fixtures/env";
import { RateLimitError } from "../errors/rate-limit-error";

type CreateSessionResponse = ComAtprotoServerCreateSession.Response;

Expand Down Expand Up @@ -103,6 +104,42 @@ describe("BlueSky", () => {

expect(bluesky).toBeInstanceOf(BlueSky);
});

it("should handle rate limiting during login", async () => {
vi.mocked(mockEnv.BLUESKY_SESSION_STORAGE.get).mockResolvedValueOnce(
null,
);

const mockRateLimitError = {
error: "RateLimitExceeded",
headers: {
"ratelimit-limit": "100",
"ratelimit-policy": "100;w=86400",
"ratelimit-remaining": "0",
"ratelimit-reset": "1733077560",
},
};

vi.mocked(agent.login).mockRejectedValueOnce(mockRateLimitError);

const promise = BlueSky.retrieveAgent(mockEnv);

await expect(promise).rejects.toThrow(RateLimitError);
await expect(promise).rejects.toThrow("Rate limited until 1733077560");
});

it("should pass through other errors during login", async () => {
vi.mocked(mockEnv.BLUESKY_SESSION_STORAGE.get).mockResolvedValueOnce(
null,
);

const originalError = new Error("Some other error");
vi.mocked(agent.login).mockRejectedValueOnce(originalError);

const promise = BlueSky.retrieveAgent(mockEnv);

await expect(promise).rejects.toThrow(originalError);
});
});

describe("postMessage", () => {
Expand Down
44 changes: 34 additions & 10 deletions src/api-wrappers/bluesky.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
} from "@atproto/api";

import { Env } from "../types";
import { RateLimitError } from "../errors/rate-limit-error";
import { BlueskyRateLimitExceededError } from "../types/bluesky";

const SESSION_KEY = "session";

Expand All @@ -28,20 +30,30 @@ export class BlueSky {
}

static async retrieveAgent(env: Env): Promise<BlueSky> {
const bluesky = new BlueSky(env);
try {
const bluesky = new BlueSky(env);

const existingSessionData =
await env.BLUESKY_SESSION_STORAGE.get(SESSION_KEY);
const existingSessionData =
await env.BLUESKY_SESSION_STORAGE.get(SESSION_KEY);

if (existingSessionData) {
const sessionData = JSON.parse(existingSessionData);
if (existingSessionData) {
const sessionData = JSON.parse(existingSessionData);

await bluesky.agent.resumeSession(sessionData);
} else {
await bluesky.login(env.BSKY_USERNAME, env.BSKY_PASSWORD);
}
await bluesky.agent.resumeSession(sessionData);
} else {
await bluesky.login(env.BSKY_USERNAME, env.BSKY_PASSWORD);
}

return bluesky;
return bluesky;
} catch (error: unknown) {
if (isRateLimitError(error)) {
throw new RateLimitError(
`Rate limited until ${error.headers["ratelimit-reset"]}`,
);
}

throw error;
}
}

async getProfile(): Promise<
Expand Down Expand Up @@ -94,3 +106,15 @@ export class BlueSky {
});
}
}

const isRateLimitError = (
error: unknown,
): error is BlueskyRateLimitExceededError => {
return (
typeof error === "object" &&
error !== null &&
"error" in error &&
error.error === "RateLimitExceeded" &&
"headers" in error
);
};
6 changes: 6 additions & 0 deletions src/errors/rate-limit-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class RateLimitError extends Error {
constructor(message: string) {
super(message);
this.name = "RateLimitError";
}
}
10 changes: 10 additions & 0 deletions src/types/bluesky.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface BlueskyRateLimitExceededError {
name: "RateLimitExceeded";
headers: {
"ratelimit-limit": string;
"ratelimit-policy": string;
"ratelimit-remaining": string;
"ratelimit-reset": string;
};
statusText: string;
}

0 comments on commit 482ebae

Please sign in to comment.