mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
feat(dev): Add database seeding for development and testing
- Add seedLibraryItems function for populating test data - Create sample library items with realistic content - Add library controller for REST endpoints - Support development workflow with consistent test data
This commit is contained in:
parent
41db30c856
commit
905d1892af
2 changed files with 189 additions and 0 deletions
148
packages/api-nest/src/database/seeds/library-items.seed.ts
Normal file
148
packages/api-nest/src/database/seeds/library-items.seed.ts
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
import { randomUUID } from 'crypto'
|
||||
import { DataSource } from 'typeorm'
|
||||
import {
|
||||
LibraryItemEntity,
|
||||
LibraryItemState,
|
||||
ContentReaderType,
|
||||
} from '../../library/entities/library-item.entity'
|
||||
|
||||
/**
|
||||
* Seed example library items for a user (for testing)
|
||||
* Usage: Call this function after user registration in development
|
||||
*/
|
||||
export async function seedLibraryItems(
|
||||
dataSource: DataSource,
|
||||
userId: string,
|
||||
): Promise<LibraryItemEntity[]> {
|
||||
console.log(`[Seed] Starting seed for user ${userId}...`)
|
||||
|
||||
const libraryRepository = dataSource.getRepository(LibraryItemEntity)
|
||||
|
||||
const sampleItems = [
|
||||
{
|
||||
userId,
|
||||
title: 'Building Scalable NestJS Applications',
|
||||
slug: 'building-scalable-nestjs-applications',
|
||||
originalUrl: 'https://docs.nestjs.com/techniques/database',
|
||||
author: 'NestJS Team',
|
||||
description:
|
||||
'Learn how to build scalable, maintainable applications with NestJS framework',
|
||||
state: LibraryItemState.SUCCEEDED,
|
||||
contentReader: ContentReaderType.WEB,
|
||||
folder: 'inbox',
|
||||
itemType: 'ARTICLE',
|
||||
wordCount: 2500,
|
||||
siteName: 'NestJS Docs',
|
||||
readingProgressTopPercent: 0,
|
||||
readingProgressBottomPercent: 0,
|
||||
savedAt: new Date(Date.now() - 1000 * 60 * 60), // 1 hour ago
|
||||
},
|
||||
{
|
||||
userId,
|
||||
title: 'GraphQL Best Practices 2025',
|
||||
slug: 'graphql-best-practices-2025',
|
||||
originalUrl: 'https://graphql.org/learn/best-practices/',
|
||||
author: 'GraphQL Foundation',
|
||||
description:
|
||||
'Modern best practices for designing and implementing GraphQL APIs',
|
||||
state: LibraryItemState.SUCCEEDED,
|
||||
contentReader: ContentReaderType.WEB,
|
||||
folder: 'inbox',
|
||||
itemType: 'ARTICLE',
|
||||
wordCount: 3200,
|
||||
siteName: 'GraphQL.org',
|
||||
readingProgressTopPercent: 45,
|
||||
readingProgressBottomPercent: 40,
|
||||
savedAt: new Date(Date.now() - 1000 * 60 * 60 * 3), // 3 hours ago
|
||||
},
|
||||
{
|
||||
userId,
|
||||
title: 'Understanding React Server Components',
|
||||
slug: 'understanding-react-server-components',
|
||||
originalUrl:
|
||||
'https://react.dev/reference/react/use-server',
|
||||
author: 'React Team',
|
||||
description:
|
||||
'Deep dive into React Server Components and their impact on modern web applications',
|
||||
state: LibraryItemState.SUCCEEDED,
|
||||
contentReader: ContentReaderType.WEB,
|
||||
folder: 'inbox',
|
||||
itemType: 'ARTICLE',
|
||||
wordCount: 4100,
|
||||
siteName: 'React.dev',
|
||||
readingProgressTopPercent: 100,
|
||||
readingProgressBottomPercent: 100,
|
||||
readAt: new Date(Date.now() - 1000 * 60 * 60 * 24), // 1 day ago
|
||||
savedAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 2), // 2 days ago
|
||||
},
|
||||
{
|
||||
userId,
|
||||
title: 'TypeScript 5.8 Release Notes',
|
||||
slug: 'typescript-5-8-release-notes',
|
||||
originalUrl: 'https://devblogs.microsoft.com/typescript/',
|
||||
author: 'TypeScript Team',
|
||||
description:
|
||||
'New features and improvements in TypeScript 5.8 release',
|
||||
state: LibraryItemState.SUCCEEDED,
|
||||
contentReader: ContentReaderType.WEB,
|
||||
folder: 'archive',
|
||||
itemType: 'ARTICLE',
|
||||
wordCount: 1800,
|
||||
siteName: 'TypeScript Blog',
|
||||
readingProgressTopPercent: 100,
|
||||
readingProgressBottomPercent: 100,
|
||||
readAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 3), // 3 days ago
|
||||
savedAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5), // 5 days ago
|
||||
},
|
||||
{
|
||||
userId,
|
||||
title: 'PostgreSQL Performance Tuning Guide',
|
||||
slug: 'postgresql-performance-tuning-guide',
|
||||
originalUrl: 'https://www.postgresql.org/docs/current/performance-tips.html',
|
||||
author: 'PostgreSQL Community',
|
||||
description:
|
||||
'Comprehensive guide to optimizing PostgreSQL database performance',
|
||||
state: LibraryItemState.SUCCEEDED,
|
||||
contentReader: ContentReaderType.WEB,
|
||||
folder: 'inbox',
|
||||
itemType: 'ARTICLE',
|
||||
wordCount: 5400,
|
||||
siteName: 'PostgreSQL Docs',
|
||||
readingProgressTopPercent: 15,
|
||||
readingProgressBottomPercent: 10,
|
||||
savedAt: new Date(Date.now() - 1000 * 60 * 60 * 12), // 12 hours ago
|
||||
},
|
||||
]
|
||||
|
||||
const createdItems: LibraryItemEntity[] = []
|
||||
|
||||
for (const itemData of sampleItems) {
|
||||
try {
|
||||
console.log(`[Seed] Attempting to create: ${itemData.title}`)
|
||||
|
||||
const item = libraryRepository.create({
|
||||
id: randomUUID(), // Manually generate UUID like E2E tests do
|
||||
...itemData,
|
||||
user: { id: userId } as any, // TypeORM relation requirement
|
||||
readingProgressLastReadAnchor: itemData.readingProgressTopPercent > 0 ? 1 : 0,
|
||||
readingProgressHighestReadAnchor: itemData.readingProgressTopPercent > 0 ? 1 : 0,
|
||||
})
|
||||
|
||||
console.log(`[Seed] Created entity object, now saving...`)
|
||||
const saved = await libraryRepository.save(item)
|
||||
console.log(`[Seed] ✓ Created item: ${saved.title} (ID: ${saved.id})`)
|
||||
createdItems.push(saved)
|
||||
} catch (error: any) {
|
||||
console.error(`[Seed] ✗ Failed to create item "${itemData.title}":`)
|
||||
console.error(`[Seed] Error name: ${error?.name}`)
|
||||
console.error(`[Seed] Error message: ${error?.message}`)
|
||||
if (error?.detail) console.error(`[Seed] Error detail: ${error.detail}`)
|
||||
if (error?.constraint) console.error(`[Seed] Constraint: ${error.constraint}`)
|
||||
console.error(`[Seed] Full error:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ [Seed] Completed: ${createdItems.length}/${sampleItems.length} items created for user ${userId}`)
|
||||
|
||||
return createdItems
|
||||
}
|
||||
41
packages/api-nest/src/library/library.controller.ts
Normal file
41
packages/api-nest/src/library/library.controller.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { Controller, Post, UseGuards, HttpCode } from '@nestjs/common'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'
|
||||
import { CurrentUser } from '../user/decorators/current-user.decorator'
|
||||
import { User } from '../user/entities/user.entity'
|
||||
import { seedLibraryItems } from '../database/seeds/library-items.seed'
|
||||
|
||||
@Controller('library')
|
||||
export class LibraryController {
|
||||
constructor(private readonly dataSource: DataSource) {}
|
||||
|
||||
/**
|
||||
* Development endpoint to seed example library items for current user
|
||||
* DELETE this endpoint before production deployment
|
||||
*/
|
||||
@Post('seed')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@HttpCode(200)
|
||||
async seedLibrary(@CurrentUser() user: User) {
|
||||
console.log(`[Controller] Seeding library for user: ${user.id}`)
|
||||
|
||||
try {
|
||||
const items = await seedLibraryItems(this.dataSource, user.id)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Seeded ${items.length} example library items`,
|
||||
itemIds: items.map((item) => item.id),
|
||||
items: items.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
folder: item.folder,
|
||||
state: item.state,
|
||||
})),
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Controller] Seed failed:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue