Skip to content

Commit

Permalink
feat(egh): update lesson schema and refactor Typesense lesson upsert
Browse files Browse the repository at this point in the history
- Changed `published_at` field type from `number` to `string` in `updateEggheadLesson` function to store ISO date format.
- Updated `writePostUpdateToDatabase` to use ISO string for `published_at`.
- Introduced new `typesense-query.ts` file for managing Typesense operations, including upserting posts.
- Enhanced `InstructorSchema` and `TypesensePostSchema` to include additional instructor details and ensure proper validation.
- Refactored import paths for better organization.
  • Loading branch information
Creeland authored and kodiakhq[bot] committed Dec 13, 2024
1 parent 2d0379f commit 214966d
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 65 deletions.
6 changes: 5 additions & 1 deletion apps/egghead/src/lib/egghead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export async function updateEggheadLesson(input: {
duration: number
hlsUrl?: string
body?: string
published_at?: number
published_at?: string
}) {
const {
eggheadLessonId,
Expand Down Expand Up @@ -270,6 +270,10 @@ export const eggheadLessonSchema = z.object({
state: z.string(),
instructor: z.object({
id: z.number(),
name: z.string(),
url: z.string().url(),
avatar_url: z.string().url(),
avatar_file_name: z.string(),
}),
})

Expand Down
4 changes: 2 additions & 2 deletions apps/egghead/src/lib/posts-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import {
updateSanityLesson,
} from './sanity-content-query'
import { EggheadTag, EggheadTagSchema } from './tags'
import { upsertPostToTypeSense } from './typesense'
import { upsertPostToTypeSense } from './typesense-query'

export async function searchLessons(searchTerm: string) {
const { session } = await getServerAuthSession()
Expand Down Expand Up @@ -518,7 +518,7 @@ export async function writePostUpdateToDatabase(input: {
hlsUrl: `https://stream.mux.com/${videoResource.muxPlaybackId}.m3u8`,
}),
...(action === 'publish' && {
published_at: Date.now(),
published_at: new Date().toISOString(),
}),
})
}
Expand Down
94 changes: 94 additions & 0 deletions apps/egghead/src/lib/typesense-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import Typesense from 'typesense'

import { getEggheadLesson } from './egghead'
import { Post, PostAction } from './posts'
import { getPostTags } from './posts-query'
import { InstructorSchema, TypesensePostSchema } from './typesense'

export async function upsertPostToTypeSense(post: Post, action: PostAction) {
let client = new Typesense.Client({
nodes: [
{
host: process.env.NEXT_PUBLIC_TYPESENSE_HOST!,
port: 443,
protocol: 'https',
},
],
apiKey: process.env.TYPESENSE_WRITE_API_KEY!,
connectionTimeoutSeconds: 2,
})

const shouldIndex =
post.fields.state === 'published' && post.fields.visibility === 'public'

if (!shouldIndex) {
await client
.collections(process.env.TYPESENSE_COLLECTION_NAME!)
.documents(String(post.fields.eggheadLessonId))
.delete()
.catch((err) => {
console.error(err)
})
} else {
if (!post.fields.eggheadLessonId) {
return
}

const lesson = await getEggheadLesson(post.fields.eggheadLessonId)

console.log('lesson', lesson)

const instructor = InstructorSchema.parse({
id: lesson.instructor?.id,
name: lesson.instructor?.full_name,
first_name: lesson.instructor?.first_name,
last_name: lesson.instructor?.last_name,
url: lesson.instructor?.url,
avatar_url: lesson.instructor?.avatar_url,
})

const tags = await getPostTags(post.id)

const resource = TypesensePostSchema.safeParse({
id: `${post.fields.eggheadLessonId}`,
externalId: post.id,
title: post.fields.title,
slug: post.fields.slug,
summary: post.fields.description,
description: post.fields.body,
name: post.fields.title,
path: `/${post.fields.slug}`,
type: post.fields.postType,
_tags: tags.map((tag) => tag.fields.name),
...(lesson && {
instructor_name: lesson.instructor?.full_name,
instructor,
image: lesson.image_480_url,
published_at_timestamp: lesson.published_at
? new Date(lesson.published_at).getTime()
: null,
}),
})

if (!resource.success) {
console.error(resource.error)
return
}

console.log('resource', resource.data)

await client
.collections(process.env.TYPESENSE_COLLECTION_NAME!)
.documents()
.upsert({
...resource.data,
...(action === 'publish' && {
published_at_timestamp: post.updatedAt?.getTime() ?? Date.now(),
}),
updated_at_timestamp: post.updatedAt?.getTime() ?? Date.now(),
})
.catch((err) => {
console.error(err)
})
}
}
89 changes: 27 additions & 62 deletions apps/egghead/src/lib/typesense.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,30 @@
import Typesense from 'typesense'
import * as z from 'zod'

import { getEggheadLesson } from './egghead'
import { Post, PostAction } from './posts'
export const InstructorSchema = z.object({
id: z.number().optional(),
name: z.string().optional(),
first_name: z.string().optional(),
last_name: z.string().optional(),
full_name: z.string().optional(),
avatar_url: z.string().url().optional(),
})

export async function upsertPostToTypeSense(post: Post, action: PostAction) {
let client = new Typesense.Client({
nodes: [
{
host: process.env.NEXT_PUBLIC_TYPESENSE_HOST!,
port: 443,
protocol: 'https',
},
],
apiKey: process.env.TYPESENSE_WRITE_API_KEY!,
connectionTimeoutSeconds: 2,
})
export const TypesensePostSchema = z.object({
type: z.string().optional(),
id: z.string().optional(),
name: z.string().optional(),
title: z.string().optional(),
slug: z.string().optional(),
externalId: z.string().optional(),
description: z.string().optional(),
summary: z.string().optional(),
image: z.string().optional(),
_tags: z.array(z.string()).optional(),
instructor: InstructorSchema.optional(),
instructor_name: z.string().optional(),
instructor_url: z.string().url().optional(),
path: z.string().optional(),
published_at_timestamp: z.number().nullish(),
})

const shouldIndex =
post.fields.state === 'published' && post.fields.visibility === 'public'

if (!shouldIndex) {
await client
.collections(process.env.TYPESENSE_COLLECTION_NAME!)
.documents(String(post.fields.eggheadLessonId))
.delete()
.catch((err) => {
console.error(err)
})
} else {
if (!post.fields.eggheadLessonId) {
return
}
const lesson = await getEggheadLesson(post.fields.eggheadLessonId)
const resource = {
id: `${post.fields.eggheadLessonId}`,
externalId: post.id,
title: post.fields.title,
slug: post.fields.slug,
summary: post.fields.description,
description: post.fields.body,
name: post.fields.title,
path: `/${post.fields.slug}`,
type: post.fields.postType,
...(lesson && {
instructor_name: lesson.instructor?.full_name,
instructor: lesson.instructor,
image: lesson.image_480_url,
}),
}
await client
.collections(process.env.TYPESENSE_COLLECTION_NAME!)
.documents()
.upsert({
...resource,
...(action === 'publish' && {
published_at_timestamp: post.updatedAt?.getTime() ?? Date.now(),
}),
updated_at_timestamp: post.updatedAt?.getTime() ?? Date.now(),
})
.catch((err) => {
console.error(err)
})
}
}
export type TypesensePost = z.infer<typeof TypesensePostSchema>

0 comments on commit 214966d

Please sign in to comment.