Skip to content

Commit 2ed4fbf

Browse files
committed
feat(api): align sources table schema with data model
The `sources` table in the database was overly simplistic and did not reflect the rich `Source` data model, leading to data loss and seeding errors. This change expands the `sources` table schema to include all fields from the `Source` model, such as `description`, `url`, and `status`. It correctly handles nested objects by storing `source_type` as `JSONB` and creating a foreign key relationship for `headquarters` to the `countries` table. The seeding logic has been updated to populate these new columns, resolving the architectural mismatch.
1 parent c5a505a commit 2ed4fbf

File tree

1 file changed

+58
-8
lines changed

1 file changed

+58
-8
lines changed

lib/src/services/database_seeding_service.dart

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,14 @@ class DatabaseSeedingService {
7373
await _connection.execute('''
7474
CREATE TABLE IF NOT EXISTS sources (
7575
id TEXT PRIMARY KEY,
76-
name TEXT NOT NULL UNIQUE,
76+
name TEXT NOT NULL,
77+
description TEXT,
78+
url TEXT,
79+
language TEXT,
80+
status TEXT,
81+
type TEXT,
82+
source_type JSONB,
83+
headquarters_country_id TEXT REFERENCES countries(id),
7784
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
7885
updated_at TIMESTAMPTZ
7986
);
@@ -187,12 +194,31 @@ class DatabaseSeedingService {
187194
_log.fine('Seeding sources...');
188195
for (final data in sourcesFixturesData) {
189196
final source = Source.fromJson(data);
197+
final params = source.toJson();
198+
199+
// The `headquarters` field in the model is a nested `Country` object.
200+
// We store its ID in the `headquarters_country_id` column.
201+
params['headquarters_country_id'] = source.headquarters?.id;
202+
203+
// Ensure optional fields exist for the postgres driver.
204+
params.putIfAbsent('description', () => null);
205+
params.putIfAbsent('url', () => null);
206+
params.putIfAbsent('language', () => null);
207+
params.putIfAbsent('source_type', () => null);
208+
params.putIfAbsent('status', () => null);
209+
params.putIfAbsent('type', () => null);
210+
params.putIfAbsent('updated_at', () => null);
211+
190212
await _connection.execute(
191213
Sql.named(
192-
'INSERT INTO sources (id, name) VALUES (@id, @name) '
214+
'INSERT INTO sources (id, name, description, url, language, '
215+
'status, type, source_type, headquarters_country_id, '
216+
'created_at, updated_at) VALUES (@id, @name, @description, '
217+
'@url, @language, @status, @type, @source_type, '
218+
'@headquarters_country_id, @created_at, @updated_at) '
193219
'ON CONFLICT (id) DO NOTHING',
194220
),
195-
parameters: source.toJson(),
221+
parameters: params,
196222
);
197223
}
198224

@@ -259,14 +285,20 @@ class DatabaseSeedingService {
259285
try {
260286
// Seed AppConfig
261287
_log.fine('Seeding AppConfig...');
262-
final appConfig = AppConfig.fromJson(appConfigFixtureData);
288+
final appConfig = AppConfig.fromJson(appConfigFixtureData);
289+
// The `app_config` table only has `id` and `user_preference_limits`.
290+
// We must provide an explicit map to avoid a "superfluous variables"
291+
// error from the postgres driver.
263292
await _connection.execute(
264293
Sql.named(
265294
'INSERT INTO app_config (id, user_preference_limits) '
266295
'VALUES (@id, @user_preference_limits) '
267296
'ON CONFLICT (id) DO NOTHING',
268297
),
269-
parameters: appConfig.toJson(),
298+
parameters: {
299+
'id': appConfig.id,
300+
'user_preference_limits': appConfig.userPreferenceLimits.toJson(),
301+
},
270302
);
271303

272304
// Seed Admin User
@@ -276,13 +308,19 @@ class DatabaseSeedingService {
276308
(user) => user.roles.contains(UserRoles.admin),
277309
orElse: () => throw StateError('Admin user not found in fixtures.'),
278310
);
311+
// The `users` table only has `id`, `email`, and `roles`. We must
312+
// provide an explicit map to avoid a "superfluous variables" error.
279313
await _connection.execute(
280314
Sql.named(
281315
'INSERT INTO users (id, email, roles) '
282316
'VALUES (@id, @email, @roles) '
283317
'ON CONFLICT (id) DO NOTHING',
284318
),
285-
parameters: adminUser.toJson(),
319+
parameters: {
320+
'id': adminUser.id,
321+
'email': adminUser.email,
322+
'roles': adminUser.roles,
323+
},
286324
);
287325

288326
// Seed default settings and preferences for the admin user.
@@ -296,7 +334,12 @@ class DatabaseSeedingService {
296334
'VALUES (@id, @user_id, @display_settings, @language) '
297335
'ON CONFLICT (id) DO NOTHING',
298336
),
299-
parameters: {...adminSettings.toJson(), 'user_id': adminUser.id},
337+
parameters: {
338+
'id': adminSettings.id,
339+
'user_id': adminUser.id,
340+
'display_settings': adminSettings.displaySettings.toJson(),
341+
'language': adminSettings.language.toJson(),
342+
},
300343
);
301344

302345
await _connection.execute(
@@ -307,7 +350,14 @@ class DatabaseSeedingService {
307350
'@followed_sources, @followed_countries, @saved_headlines) '
308351
'ON CONFLICT (id) DO NOTHING',
309352
),
310-
parameters: {...adminPreferences.toJson(), 'user_id': adminUser.id},
353+
parameters: {
354+
'id': adminPreferences.id,
355+
'user_id': adminUser.id,
356+
'followed_categories': adminPreferences.followedCategories,
357+
'followed_sources': adminPreferences.followedSources,
358+
'followed_countries': adminPreferences.followedCountries,
359+
'saved_headlines': adminPreferences.savedHeadlines,
360+
},
311361
);
312362

313363
await _connection.execute('COMMIT');

0 commit comments

Comments
 (0)