article.service.ts•6.06 kB
/**
* Article service for managing article data
*/
import { Article } from '../models/prisma.types';
import { repositoryFactory } from '../repositories';
import { logger } from '../utils/logger';
import { NewsApiResponse, NewsArticle, SingleArticleResponse } from '../models/news.model';
/**
* Service for managing article data
*/
export class ArticleService {
private articleRepository = repositoryFactory.getArticleRepository();
/**
* Save articles from API response to database
* @param apiResponse Response from News API
* @returns Number of articles saved
*/
async saveArticlesFromApiResponse(apiResponse: NewsApiResponse): Promise<number> {
try {
let savedCount = 0;
// Process each article in the API response
for (const article of apiResponse.data) {
await this.saveArticleFromApiData(article);
savedCount++;
}
logger.info(`Saved ${savedCount} articles to database`);
return savedCount;
} catch (error) {
logger.error('Error saving articles from API response:', error);
throw error;
}
}
/**
* Save single article from API response to database
* @param apiArticle Article data from API
* @returns Saved article
*/
async saveArticleFromApiData(apiArticle: NewsArticle): Promise<Article> {
try {
// Check if article already exists
const existingArticle = await this.articleRepository.findByUuid(apiArticle.uuid);
if (existingArticle) {
// Update existing article
return await this.articleRepository.update(existingArticle.id, {
title: apiArticle.title,
description: apiArticle.description || null,
url: apiArticle.url,
imageUrl: apiArticle.image_url || null,
publishedAt: new Date(apiArticle.published_at),
updatedAt: new Date()
});
} else {
// Create new article
return await this.articleRepository.create({
uuid: apiArticle.uuid,
title: apiArticle.title,
description: apiArticle.description || null,
url: apiArticle.url,
imageUrl: apiArticle.image_url || null,
source: apiArticle.source,
publishedAt: new Date(apiArticle.published_at),
categories: Array.isArray(apiArticle.categories) ? apiArticle.categories.join(',') : '',
language: apiArticle.language,
readCount: 0,
relevanceScore: apiArticle.relevance_score || null,
sentiment: null // To be filled by sentiment analysis later
});
}
} catch (error) {
logger.error(`Error saving article ${apiArticle.uuid}:`, error);
throw error;
}
}
/**
* Save single article from UUID API response
* @param apiArticle Article data from UUID API
* @returns Saved article
*/
async saveArticleFromUuidResponse(apiArticle: SingleArticleResponse): Promise<Article> {
try {
// Convert SingleArticleResponse to the format needed for saving
const articleData: NewsArticle = {
uuid: apiArticle.uuid,
title: apiArticle.title,
description: apiArticle.description,
keywords: apiArticle.keywords,
snippet: apiArticle.snippet,
url: apiArticle.url,
image_url: apiArticle.image_url,
language: apiArticle.language,
published_at: apiArticle.published_at,
source: apiArticle.source,
categories: apiArticle.categories,
relevance_score: null,
locale: apiArticle.language // Use language as locale if not provided
};
return await this.saveArticleFromApiData(articleData);
} catch (error) {
logger.error(`Error saving article from UUID response ${apiArticle.uuid}:`, error);
throw error;
}
}
/**
* Get article by UUID
* @param uuid Article UUID
* @returns Article or null if not found
*/
async getArticleByUuid(uuid: string): Promise<Article | null> {
try {
return await this.articleRepository.findByUuid(uuid);
} catch (error) {
logger.error(`Error getting article by UUID ${uuid}:`, error);
throw error;
}
}
/**
* Get recent articles
* @param limit Maximum number of articles to return
* @returns Array of recent articles
*/
async getRecentArticles(limit: number = 10): Promise<Article[]> {
try {
const result = await this.articleRepository.search({
take: limit,
orderBy: { publishedAt: 'desc' }
});
return result.items;
} catch (error) {
logger.error('Error getting recent articles:', error);
throw error;
}
}
/**
* Get articles by category
* @param category Article category
* @param limit Maximum number of articles to return
* @returns Array of articles in the specified category
*/
async getArticlesByCategory(category: string, limit: number = 10): Promise<Article[]> {
try {
return await this.articleRepository.findByCategory(category, limit);
} catch (error) {
logger.error(`Error getting articles by category ${category}:`, error);
throw error;
}
}
/**
* Get similar articles
* @param articleId Article ID to find similar articles for
* @param limit Maximum number of similar articles to return
* @returns Array of similar articles
*/
async getSimilarArticles(articleId: number, limit: number = 5): Promise<Article[]> {
try {
return await this.articleRepository.findSimilarArticles(articleId, limit);
} catch (error) {
logger.error(`Error getting similar articles for article ${articleId}:`, error);
throw error;
}
}
/**
* Track article view
* @param articleId Article ID
* @returns Updated article
*/
async trackArticleView(articleId: number): Promise<Article> {
try {
return await this.articleRepository.incrementReadCount(articleId);
} catch (error) {
logger.error(`Error tracking view for article ${articleId}:`, error);
throw error;
}
}
}