264 lines
8.4 KiB
Dart
264 lines
8.4 KiB
Dart
/// News Remote DataSource
|
|
///
|
|
/// Handles fetching news/blog data from the Frappe API.
|
|
library;
|
|
|
|
import 'package:dio/dio.dart';
|
|
import 'package:worker/core/constants/api_constants.dart';
|
|
import 'package:worker/core/network/dio_client.dart';
|
|
import 'package:worker/core/services/frappe_auth_service.dart';
|
|
import 'package:worker/features/news/data/models/blog_category_model.dart';
|
|
import 'package:worker/features/news/data/models/blog_post_model.dart';
|
|
|
|
/// News Remote Data Source
|
|
///
|
|
/// Provides methods to fetch news and blog content from the Frappe API.
|
|
/// Uses FrappeAuthService for session management.
|
|
class NewsRemoteDataSource {
|
|
NewsRemoteDataSource(this._dioClient, this._frappeAuthService);
|
|
|
|
final DioClient _dioClient;
|
|
final FrappeAuthService _frappeAuthService;
|
|
|
|
/// Get blog categories
|
|
///
|
|
/// Fetches all published blog categories from Frappe.
|
|
/// Returns a list of [BlogCategoryModel].
|
|
///
|
|
/// API endpoint: POST https://land.dbiz.com/api/method/frappe.client.get_list
|
|
/// Request body:
|
|
/// ```json
|
|
/// {
|
|
/// "doctype": "Blog Category",
|
|
/// "fields": ["title","name"],
|
|
/// "filters": {"published":1},
|
|
/// "order_by": "creation desc",
|
|
/// "limit_page_length": 0
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// Response format:
|
|
/// ```json
|
|
/// {
|
|
/// "message": [
|
|
/// {"title": "Tin tức", "name": "tin-tức"},
|
|
/// {"title": "Chuyên môn", "name": "chuyên-môn"},
|
|
/// ...
|
|
/// ]
|
|
/// }
|
|
/// ```
|
|
Future<List<BlogCategoryModel>> getBlogCategories() async {
|
|
try {
|
|
// Get Frappe session headers
|
|
final headers = await _frappeAuthService.getHeaders();
|
|
|
|
// Build full API URL
|
|
final url = '${ApiConstants.baseUrl}${ApiConstants.frappeApiMethod}${ApiConstants.frappeGetList}';
|
|
|
|
final response = await _dioClient.post<Map<String, dynamic>>(
|
|
url,
|
|
data: {
|
|
'doctype': 'Blog Category',
|
|
'fields': ['title', 'name'],
|
|
'filters': {'published': 1},
|
|
'order_by': 'creation desc',
|
|
'limit_page_length': 0,
|
|
},
|
|
options: Options(headers: headers),
|
|
);
|
|
|
|
if (response.data == null) {
|
|
throw Exception('Empty response from server');
|
|
}
|
|
|
|
// Parse the response using the wrapper model
|
|
final categoriesResponse = BlogCategoriesResponse.fromJson(response.data!);
|
|
|
|
return categoriesResponse.message;
|
|
} on DioException catch (e) {
|
|
if (e.response?.statusCode == 404) {
|
|
throw Exception('Blog categories endpoint not found');
|
|
} else if (e.response?.statusCode == 500) {
|
|
throw Exception('Server error while fetching blog categories');
|
|
} else if (e.type == DioExceptionType.connectionTimeout) {
|
|
throw Exception('Connection timeout while fetching blog categories');
|
|
} else if (e.type == DioExceptionType.receiveTimeout) {
|
|
throw Exception('Response timeout while fetching blog categories');
|
|
} else {
|
|
throw Exception('Failed to fetch blog categories: ${e.message}');
|
|
}
|
|
} catch (e) {
|
|
throw Exception('Unexpected error fetching blog categories: $e');
|
|
}
|
|
}
|
|
|
|
/// Get blog posts
|
|
///
|
|
/// Fetches all published blog posts from Frappe.
|
|
/// Returns a list of [BlogPostModel].
|
|
///
|
|
/// API endpoint: POST https://land.dbiz.com/api/method/frappe.client.get_list
|
|
/// Request body:
|
|
/// ```json
|
|
/// {
|
|
/// "doctype": "Blog Post",
|
|
/// "fields": ["name","title","published_on","blogger","blog_intro","content","meta_image","meta_description","blog_category"],
|
|
/// "filters": {"published":1},
|
|
/// "order_by": "published_on desc",
|
|
/// "limit_page_length": 0
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// Response format:
|
|
/// ```json
|
|
/// {
|
|
/// "message": [
|
|
/// {
|
|
/// "name": "post-slug",
|
|
/// "title": "Post Title",
|
|
/// "published_on": "2024-01-01 10:00:00",
|
|
/// "blogger": "Author Name",
|
|
/// "blog_intro": "Short introduction...",
|
|
/// "content": "<p>Full HTML content...</p>",
|
|
/// "meta_image": "https://...",
|
|
/// "meta_description": "SEO description",
|
|
/// "blog_category": "tin-tức"
|
|
/// },
|
|
/// ...
|
|
/// ]
|
|
/// }
|
|
/// ```
|
|
Future<List<BlogPostModel>> getBlogPosts() async {
|
|
try {
|
|
// Get Frappe session headers
|
|
final headers = await _frappeAuthService.getHeaders();
|
|
|
|
// Build full API URL
|
|
const url =
|
|
'${ApiConstants.baseUrl}${ApiConstants.frappeApiMethod}${ApiConstants.frappeGetList}';
|
|
|
|
final response = await _dioClient.post<Map<String, dynamic>>(
|
|
url,
|
|
data: {
|
|
'doctype': 'Blog Post',
|
|
'fields': [
|
|
'name',
|
|
'title',
|
|
'published_on',
|
|
'blogger',
|
|
'blog_intro',
|
|
'content',
|
|
'meta_image',
|
|
'meta_description',
|
|
'blog_category',
|
|
],
|
|
'filters': {'published': 1},
|
|
'order_by': 'published_on desc',
|
|
'limit_page_length': 0,
|
|
},
|
|
options: Options(headers: headers),
|
|
);
|
|
|
|
if (response.data == null) {
|
|
throw Exception('Empty response from server');
|
|
}
|
|
|
|
// Parse the response using the wrapper model
|
|
final postsResponse = BlogPostsResponse.fromJson(response.data!);
|
|
|
|
return postsResponse.message;
|
|
} on DioException catch (e) {
|
|
if (e.response?.statusCode == 404) {
|
|
throw Exception('Blog posts endpoint not found');
|
|
} else if (e.response?.statusCode == 500) {
|
|
throw Exception('Server error while fetching blog posts');
|
|
} else if (e.type == DioExceptionType.connectionTimeout) {
|
|
throw Exception('Connection timeout while fetching blog posts');
|
|
} else if (e.type == DioExceptionType.receiveTimeout) {
|
|
throw Exception('Response timeout while fetching blog posts');
|
|
} else {
|
|
throw Exception('Failed to fetch blog posts: ${e.message}');
|
|
}
|
|
} catch (e) {
|
|
throw Exception('Unexpected error fetching blog posts: $e');
|
|
}
|
|
}
|
|
|
|
/// Get blog post detail by name
|
|
///
|
|
/// Fetches a single blog post by its unique name (slug) from Frappe.
|
|
/// Returns a [BlogPostModel].
|
|
///
|
|
/// API endpoint: POST https://land.dbiz.com/api/method/frappe.client.get
|
|
/// Request body:
|
|
/// ```json
|
|
/// {
|
|
/// "doctype": "Blog Post",
|
|
/// "name": "post-slug"
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// Response format:
|
|
/// ```json
|
|
/// {
|
|
/// "message": {
|
|
/// "name": "post-slug",
|
|
/// "title": "Post Title",
|
|
/// "published_on": "2024-01-01 10:00:00",
|
|
/// "blogger": "Author Name",
|
|
/// "blog_intro": "Short introduction...",
|
|
/// "content": "<p>Full HTML content...</p>",
|
|
/// "meta_image": "https://...",
|
|
/// "meta_description": "SEO description",
|
|
/// "blog_category": "tin-tức"
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
Future<BlogPostModel> getBlogPostDetail(String postName) async {
|
|
try {
|
|
// Get Frappe session headers
|
|
final headers = await _frappeAuthService.getHeaders();
|
|
|
|
// Build full API URL for frappe.client.get
|
|
const url =
|
|
'${ApiConstants.baseUrl}${ApiConstants.frappeApiMethod}${ApiConstants.frappeGet}';
|
|
|
|
final response = await _dioClient.post<Map<String, dynamic>>(
|
|
url,
|
|
data: {
|
|
'doctype': 'Blog Post',
|
|
'name': postName,
|
|
},
|
|
options: Options(headers: headers),
|
|
);
|
|
|
|
if (response.data == null) {
|
|
throw Exception('Empty response from server');
|
|
}
|
|
|
|
// The response has the blog post data directly in "message" field
|
|
final messageData = response.data!['message'];
|
|
if (messageData == null) {
|
|
throw Exception('Blog post not found: $postName');
|
|
}
|
|
|
|
// Parse the blog post from the message field
|
|
return BlogPostModel.fromJson(messageData as Map<String, dynamic>);
|
|
} on DioException catch (e) {
|
|
if (e.response?.statusCode == 404) {
|
|
throw Exception('Blog post not found: $postName');
|
|
} else if (e.response?.statusCode == 500) {
|
|
throw Exception('Server error while fetching blog post detail');
|
|
} else if (e.type == DioExceptionType.connectionTimeout) {
|
|
throw Exception('Connection timeout while fetching blog post detail');
|
|
} else if (e.type == DioExceptionType.receiveTimeout) {
|
|
throw Exception('Response timeout while fetching blog post detail');
|
|
} else {
|
|
throw Exception('Failed to fetch blog post detail: ${e.message}');
|
|
}
|
|
} catch (e) {
|
|
throw Exception('Unexpected error fetching blog post detail: $e');
|
|
}
|
|
}
|
|
}
|