-
Notifications
You must be signed in to change notification settings - Fork 158
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: homepage pagination and local db seeding (#221)
* run simple seed submission script * add nullable safety to prevent bad access for non-existent user and enable display of raw item userId * rename db operation with db prefix and add reset to task list * enable simple pagination and page size * use page param for future prs * fix spacing for earlier stripe task and remove accidental import statement * revert item summary changes * address pr comments * lint and var name fix * do not error when user does not exist * revert component changes and add users and scores to db to self-contain script - also add tool to print kv * update README.md * fix bad checkout from forked main vs upstream main * add avatar url for dummy users with guest profile pic from gravatar
- Loading branch information
1 parent
f2364de
commit 26d5dff
Showing
6 changed files
with
180 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright 2023 the Deno authors. All rights reserved. MIT license. | ||
// Description: Prints kv to stdout | ||
// Usage: deno run -A --unstable tools/dump_kv.ts | ||
import { kv } from "@/utils/db.ts"; | ||
|
||
export async function dumpKv() { | ||
const iter = kv.list({ prefix: [] }); | ||
const items = []; | ||
for await (const res of iter) { | ||
items.push({ [res.key.toString()]: res.value }); | ||
} | ||
console.log(`${JSON.stringify(items, null, 2)}`); | ||
} | ||
|
||
if (import.meta.main) { | ||
await dumpKv(); | ||
await kv.close(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Copyright 2023 the Deno authors. All rights reserved. MIT license. | ||
// Description: Seeds the kv db with Hacker News stories | ||
import { createItem, createUser, type Item, kv } from "@/utils/db.ts"; | ||
|
||
// Reference: https://github.com/HackerNews/API | ||
const API_BASE_URL = `https://hacker-news.firebaseio.com/v0`; | ||
|
||
interface Story { | ||
id: number; | ||
score: number; | ||
time: number; // Unix seconds | ||
by: string; | ||
title: string; | ||
url: string; | ||
} | ||
|
||
function* batchify<T>(arr: T[], n = 5): Generator<T[], void> { | ||
for (let i = 0; i < arr.length; i += n) { | ||
yield arr.slice(i, i + n); | ||
} | ||
} | ||
|
||
// Fetches the top 500 HN stories to seed the db | ||
async function fetchTopStoryIds() { | ||
const resp = await fetch(`${API_BASE_URL}/topstories.json`); | ||
if (!resp.ok) { | ||
console.error(`Failed to fetchTopStoryIds - status: ${resp.status}`); | ||
return; | ||
} | ||
return await resp.json(); | ||
} | ||
|
||
async function fetchStory(id: number | string) { | ||
const resp = await fetch(`${API_BASE_URL}/item/${id}.json`); | ||
if (!resp.ok) { | ||
console.error(`Failed to fetchStory (${id}) - status: ${resp.status}`); | ||
return; | ||
} | ||
return await resp.json(); | ||
} | ||
|
||
async function fetchTopStories(limit = 10) { | ||
const ids = await fetchTopStoryIds(); | ||
if (!(ids && ids.length)) { | ||
console.error(`No ids to fetch!`); | ||
return; | ||
} | ||
const filtered: [number] = ids.slice(0, limit); | ||
const stories: Story[] = []; | ||
for (const batch of batchify(filtered)) { | ||
stories.push(...(await Promise.all(batch.map((id) => fetchStory(id)))) | ||
.filter((v) => Boolean(v)) as Story[]); | ||
} | ||
return stories; | ||
} | ||
|
||
async function createItemWithScore(item: Item) { | ||
const res = await createItem(item); | ||
return await kv.set(["items", res!.id], { | ||
...res, | ||
score: item.score, | ||
createdAt: item.createdAt, | ||
}); | ||
} | ||
|
||
async function seedSubmissions(stories: Story[]) { | ||
const items = stories.map(({ by: userId, title, url, score, time }) => { | ||
return { | ||
userId, | ||
title, | ||
url, | ||
score, | ||
createdAt: new Date(time * 1000), | ||
} as Item; | ||
}).filter(({ url }) => url); | ||
for (const batch of batchify(items)) { | ||
await Promise.all(batch.map((item) => createItemWithScore(item))); | ||
} | ||
return items; | ||
} | ||
|
||
async function main(limit = 20) { | ||
const stories = await fetchTopStories(limit); | ||
if (!(stories && stories.length)) { | ||
console.error(`No stories to seed!`); | ||
return; | ||
} | ||
const items = await seedSubmissions(stories); | ||
|
||
// Create dummy users to ensure each post has a corresponding user | ||
for (const batch of batchify(items)) { | ||
await Promise.allSettled(batch.map(({ userId: id }) => | ||
createUser({ | ||
id, // id must match userId for post | ||
login: id, | ||
avatarUrl: "https://www.gravatar.com/avatar/?d=mp&s=64", | ||
stripeCustomerId: crypto.randomUUID(), // unique per userId | ||
sessionId: crypto.randomUUID(), // unique per userId | ||
}) // ignore errors if dummy user already exists | ||
)); | ||
} | ||
} | ||
|
||
if (import.meta.main) { | ||
await main(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters