Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5e9f921

Browse files
authored
Merge pull request #13 from headlines-toolkit/fix_database_bugs
Fix database bugs
2 parents bf23c06 + 3ff1b60 commit 5e9f921

File tree

3 files changed

+45
-17
lines changed

3 files changed

+45
-17
lines changed

‎lib/src/config/app_dependencies.dart‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,21 @@ class AppDependencies {
115115
}
116116
return Headline.fromJson(json);
117117
},
118-
(h) => h.toJson(), // toJson already handles DateTime correctly
118+
(headline) {
119+
final json = headline.toJson();
120+
// The database expects source_id and category_id, not nested objects.
121+
// We extract the IDs and remove the original objects to match the
122+
// schema.
123+
if (headline.source != null) {
124+
json['source_id'] = headline.source!.id;
125+
}
126+
if (headline.category != null) {
127+
json['category_id'] = headline.category!.id;
128+
}
129+
json.remove('source');
130+
json.remove('category');
131+
return json;
132+
},
119133
);
120134
categoryRepository = _createRepository(
121135
connection,
@@ -147,7 +161,15 @@ class AppDependencies {
147161
}
148162
return Source.fromJson(json);
149163
},
150-
(s) => s.toJson(),
164+
(source) {
165+
final json = source.toJson();
166+
// The database expects headquarters_country_id, not a nested object.
167+
// We extract the ID and remove the original object to match the
168+
// schema.
169+
json['headquarters_country_id'] = source.headquarters?.id;
170+
json.remove('headquarters');
171+
return json;
172+
},
151173
);
152174
countryRepository = _createRepository(
153175
connection,

‎lib/src/services/database_seeding_service.dart‎

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ class DatabaseSeedingService {
106106
CREATE TABLE IF NOT EXISTS headlines (
107107
id TEXT PRIMARY KEY,
108108
title TEXT NOT NULL,
109-
source_id TEXT NOT NULL,
110-
category_id TEXT NOT NULL,
109+
source_id TEXT REFERENCES sources(id),
110+
category_id TEXT REFERENCES categories(id),
111111
image_url TEXT,
112112
url TEXT,
113113
published_at TIMESTAMPTZ,
@@ -260,19 +260,10 @@ class DatabaseSeedingService {
260260
final headline = Headline.fromJson(data);
261261
final params = headline.toJson();
262262

263-
// The `source_id` and `category_id` columns are NOT NULL. If a fixture
264-
// is missing the nested source or category object, we cannot proceed.
265-
if (headline.source == null || headline.category == null) {
266-
_log.warning(
267-
'Skipping headline fixture with missing source or category ID: '
268-
'${headline.title}',
269-
);
270-
continue;
271-
}
272-
273263
// Extract IDs from nested objects and remove the objects to match schema.
274-
params['source_id'] = headline.source!.id;
275-
params['category_id'] = headline.category!.id;
264+
// These are now nullable to match the schema.
265+
params['source_id'] = headline.source?.id;
266+
params['category_id'] = headline.category?.id;
276267
params.remove('source');
277268
params.remove('category');
278269

‎routes/api/v1/data/index.dart‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ import 'package:ht_shared/ht_shared.dart';
88

99
import '../../../_middleware.dart'; // Assuming RequestId is here
1010

11+
/// Converts a camelCase string to snake_case.
12+
String _camelToSnake(String input) {
13+
return input
14+
.replaceAllMapped(
15+
RegExp(r'(?<!^)(?=[A-Z])'),
16+
(match) => '_${match.group(0)}',
17+
)
18+
.toLowerCase();
19+
}
20+
1121
/// Handles requests for the /api/v1/data collection endpoint.
1222
/// Dispatches requests to specific handlers based on the HTTP method.
1323
Future<Response> onRequest(RequestContext context) async {
@@ -109,10 +119,15 @@ Future<Response> _handleGet(
109119
final queryParams = context.request.uri.queryParameters;
110120
final startAfterId = queryParams['startAfterId'];
111121
final limitParam = queryParams['limit'];
112-
final sortBy = queryParams['sortBy'];
122+
final sortByParam = queryParams['sortBy'];
113123
final sortOrderRaw = queryParams['sortOrder']?.toLowerCase();
114124
final limit = limitParam != null ? int.tryParse(limitParam) : null;
115125

126+
// Convert sortBy from camelCase to snake_case for the database query.
127+
// This prevents errors where the client sends 'createdAt' and the database
128+
// expects 'created_at'.
129+
final sortBy = sortByParam != null ? _camelToSnake(sortByParam) : null;
130+
116131
SortOrder? sortOrder;
117132
if (sortOrderRaw != null) {
118133
if (sortOrderRaw == 'asc') {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /