Skip to content

Commit 99de620

Browse files
committed
Of course this broke socialurls
1 parent 4455b81 commit 99de620

File tree

5 files changed

+38
-51
lines changed

5 files changed

+38
-51
lines changed

src/app/(development)/workroom/page.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ export default function WorkroomPage() {
7777
year="2025"
7878
location="Amsterdam, Netherlands"
7979
/>
80+
81+
<p>SpeakerOpengraphImage / very long title</p>
82+
<ScheduleOpengraphImage
83+
session={{
84+
name: "TSC Panel - Lee Byron, GraphQL Foundation; Kewei Qu, Meta; Rob Richard, 1stDibs; Michael Staib, ChilliCream; Moderated by Sasha Solomon, Staff Software Engineer & Tech Lead",
85+
speakers: [enisdenjo, saihaj, enisdenjo, saihaj, enisdenjo, saihaj],
86+
event_type: "Keynote Sessions",
87+
}}
88+
date="September 8-10"
89+
year="2025"
90+
location="Amsterdam, Netherlands"
91+
/>
8092
</main>
8193
)
8294
}

src/app/conf/2023/_data.ts

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,18 @@
11
import "server-only"
22
import { stripHtml } from "string-strip-html"
3-
import { SchedSpeaker, ScheduleSession } from "@/app/conf/2023/types"
4-
import pLimit from "p-limit"
53

6-
async function fetchData<T>(url: string): Promise<T> {
7-
try {
8-
const response = await fetch(url, {
9-
method: "POST",
10-
headers: {
11-
"Content-Type": "application/json",
12-
"User-Agent": "GraphQL Conf / GraphQL Foundation",
13-
},
14-
cache: "force-cache",
15-
})
16-
const data = await response.json()
17-
return data
18-
} catch (error) {
19-
throw new Error(
20-
`Error fetching data from ${url}: ${(error as Error).message || (error as Error).toString()}`,
21-
)
22-
}
23-
}
4+
import { SchedSpeaker, ScheduleSession } from "@/app/conf/2023/types"
5+
import { fetchData } from "../_api/sched-client"
246

257
const token = process.env.SCHED_ACCESS_TOKEN_2023
268

27-
async function getUsernames(): Promise<string[]> {
28-
const response = await fetchData<{ username: string }[]>(
29-
`https://graphqlconf23.sched.com/api/user/list?api_key=${token}&format=json&fields=username`,
30-
)
31-
return response.map(user => user.username)
32-
}
33-
34-
const limit = pLimit(40) // rate limit is 30req/min
35-
369
async function getSpeakers(): Promise<SchedSpeaker[]> {
37-
const usernames = await getUsernames()
38-
39-
const users = await Promise.all(
40-
usernames.map(username =>
41-
limit(() => {
42-
return fetchData<SchedSpeaker>(
43-
`https://graphqlconf23.sched.com/api/user/get?api_key=${token}&by=username&term=${username}&format=json&fields=username,company,position,name,about,location,url,avatar,role,socialurls`,
44-
)
45-
}),
46-
),
10+
const users = await fetchData<SchedSpeaker[]>(
11+
`https://graphqlconf23.sched.com/api/user/list?api_key=${token}&format=json&fields=username,company,position,name,about,location,url,avatar,role,socialurls`,
4712
)
4813

4914
const result = users
50-
.filter(speaker => speaker.role.includes("speaker"))
15+
.filter(user => user.role.includes("speaker"))
5116
.map(user => {
5217
return {
5318
...user,

src/app/conf/2025/_data.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { fetchData } from "../_api/sched-client"
66
import { speakers as speakers2024 } from "../2024/_data"
77
import { speakers as speakers2023 } from "../2023/_data"
88

9-
const USE_2025 = true
9+
const USE_2025 = false || process.env.USE_2025 === "true"
1010

1111
const apiUrl = USE_2025
1212
? "https://graphqlconf2025.sched.com/api"
@@ -68,6 +68,8 @@ function preprocessDescription(description: string | undefined | null): string {
6868

6969
export const speakers = await getSpeakers()
7070

71+
console.log("speakers", speakers)
72+
7173
// TODO: Collect tags from schedule for speakers.
7274
export const schedule = await getSchedule()
7375

src/app/conf/2025/components/speaker-opengraph-image.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ export default function SpeakerOpengraphImage({
4747
</div>
4848
</div>
4949

50-
<footer className="flex items-center border-t border-neu-300 px-16 py-8 pl-10">
50+
<footer className="flex items-center border-t-2 border-neu-300 px-16 py-8 pl-10">
5151
<span className="font-mono text-2xl font-normal uppercase leading-none text-neu-900">
5252
Speakers
5353
</span>
5454
</footer>
5555
</div>
5656

5757
{speaker.avatar && (
58-
<div className="relative flex overflow-hidden">
58+
<div className="relative flex overflow-hidden border-l-2 border-neu-300">
5959
<div className="absolute inset-0 z-[1] bg-sec-lighter mix-blend-multiply" />
6060
<img
6161
src={speaker.avatar}

src/app/conf/_api/sched-client.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
* Sched's API has a rate limit of 30 requests per minute.
33
*/
44

5-
let last429Timestamp: number | null = null
6-
const RATE_LIMIT_MS = 60_000
5+
let rateLimitResetAt: number | null = null
76

87
type RequestURL = string
98
type RequestPending = Promise<unknown>
@@ -17,8 +16,8 @@ const requestsRetried = new Map<
1716

1817
export async function fetchData<T>(url: string): Promise<T> {
1918
try {
20-
if (last429Timestamp && Date.now() - last429Timestamp < RATE_LIMIT_MS) {
21-
const wait = RATE_LIMIT_MS - (Date.now() - last429Timestamp)
19+
if (rateLimitResetAt && Date.now() < rateLimitResetAt) {
20+
const wait = rateLimitResetAt - Date.now()
2221
await new Promise(resolve => setTimeout(resolve, wait))
2322
}
2423

@@ -34,31 +33,40 @@ export async function fetchData<T>(url: string): Promise<T> {
3433
// Take note that this is feasible only because
3534
// we're currently only using this API at build time.
3635
if (response.status === 429) {
37-
last429Timestamp = Date.now()
36+
let wait = 60_000
37+
const xRateLimitResetAt = response.headers.get("x-rate-limit-reset-at")
38+
if (xRateLimitResetAt) {
39+
rateLimitResetAt = Number(xRateLimitResetAt) * 1000
40+
console.warn(
41+
`Rate limit reset at ${new Date(rateLimitResetAt).toISOString()}`,
42+
)
43+
wait = rateLimitResetAt - Date.now()
44+
}
3845

3946
const later = Promise.withResolvers<T>()
4047
requestsRetried.set(url, later.promise)
4148
const queueSize = requestsRetried.size
4249

4350
console.warn(
44-
`Rate limit exceeded, retrying in 60 seconds: ${url} (queue size: ${queueSize})`,
51+
`Rate limit exceeded, retrying in ${Math.round(wait / 1000)}s: ${url} (queue size: ${queueSize})`,
4552
)
4653

4754
const size = Object.entries(requestsRetried).filter(
4855
([_, status]) => status instanceof Promise,
4956
).length
5057

5158
console.warn(`${size} requests retrying...`)
59+
5260
setTimeout(
5361
() =>
5462
later.resolve(
5563
fetchData<T>(url).then(data => {
64+
rateLimitResetAt = null
5665
requestsRetried.set(url, "ok")
57-
last429Timestamp = null
5866
return data
5967
}),
6068
),
61-
60_000,
69+
wait,
6270
)
6371

6472
return later.promise

0 commit comments

Comments
 (0)