Skip to content

Commit 04189f3

Browse files
committed
feat(data): implement global fixture data seeding
- Implements the `seedGlobalFixtureData` method in `DatabaseSeedingService` to populate the database with initial categories, sources, countries, and headlines from the shared fixtures. - Uses `ON CONFLICT DO NOTHING` to make the seeding process idempotent. - Fixes the `createTables` method by replacing the problematic `.transaction()` helper with a manual `BEGIN`/`COMMIT`/`ROLLBACK` block for compatibility and robustness.
1 parent 8c2c579 commit 04189f3

File tree

1 file changed

+103
-10
lines changed

1 file changed

+103
-10
lines changed

lib/src/services/database_seeding_service.dart

Lines changed: 103 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,14 @@ class DatabaseSeedingService {
2828
Future<void> createTables() async {
2929
_log.info('Starting database schema creation...');
3030
try {
31-
await _connection.transaction((ctx) async {
31+
// Manually handle the transaction with BEGIN/COMMIT/ROLLBACK.
32+
await _connection.execute('BEGIN');
33+
34+
try {
3235
_log.fine('Creating "users" table...');
33-
await ctx.execute('''
36+
// All statements are executed on the main connection within the
37+
// manual transaction.
38+
await _connection.execute('''
3439
CREATE TABLE IF NOT EXISTS users (
3540
id TEXT PRIMARY KEY,
3641
email TEXT UNIQUE,
@@ -41,7 +46,7 @@ class DatabaseSeedingService {
4146
''');
4247

4348
_log.fine('Creating "app_config" table...');
44-
await ctx.execute('''
49+
await _connection.execute('''
4550
CREATE TABLE IF NOT EXISTS app_config (
4651
id TEXT PRIMARY KEY,
4752
user_preference_limits JSONB NOT NULL,
@@ -51,7 +56,7 @@ class DatabaseSeedingService {
5156
''');
5257

5358
_log.fine('Creating "categories" table...');
54-
await ctx.execute('''
59+
await _connection.execute('''
5560
CREATE TABLE IF NOT EXISTS categories (
5661
id TEXT PRIMARY KEY,
5762
name TEXT NOT NULL UNIQUE,
@@ -61,7 +66,7 @@ class DatabaseSeedingService {
6166
''');
6267

6368
_log.fine('Creating "sources" table...');
64-
await ctx.execute('''
69+
await _connection.execute('''
6570
CREATE TABLE IF NOT EXISTS sources (
6671
id TEXT PRIMARY KEY,
6772
name TEXT NOT NULL UNIQUE,
@@ -71,7 +76,7 @@ class DatabaseSeedingService {
7176
''');
7277

7378
_log.fine('Creating "countries" table...');
74-
await ctx.execute('''
79+
await _connection.execute('''
7580
CREATE TABLE IF NOT EXISTS countries (
7681
id TEXT PRIMARY KEY,
7782
name TEXT NOT NULL UNIQUE,
@@ -82,7 +87,7 @@ class DatabaseSeedingService {
8287
''');
8388

8489
_log.fine('Creating "headlines" table...');
85-
await ctx.execute('''
90+
await _connection.execute('''
8691
CREATE TABLE IF NOT EXISTS headlines (
8792
id TEXT PRIMARY KEY,
8893
title TEXT NOT NULL,
@@ -99,7 +104,7 @@ class DatabaseSeedingService {
99104
''');
100105

101106
_log.fine('Creating "user_app_settings" table...');
102-
await ctx.execute('''
107+
await _connection.execute('''
103108
CREATE TABLE IF NOT EXISTS user_app_settings (
104109
id TEXT PRIMARY KEY,
105110
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
@@ -111,7 +116,7 @@ class DatabaseSeedingService {
111116
''');
112117

113118
_log.fine('Creating "user_content_preferences" table...');
114-
await ctx.execute('''
119+
await _connection.execute('''
115120
CREATE TABLE IF NOT EXISTS user_content_preferences (
116121
id TEXT PRIMARY KEY,
117122
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
@@ -123,7 +128,13 @@ class DatabaseSeedingService {
123128
updated_at TIMESTAMPTZ
124129
);
125130
''');
126-
});
131+
132+
await _connection.execute('COMMIT');
133+
} catch (e) {
134+
// If any query inside the transaction fails, roll back.
135+
await _connection.execute('ROLLBACK');
136+
rethrow; // Re-throw the original error
137+
}
127138
_log.info('Database schema creation completed successfully.');
128139
} on Object catch (e, st) {
129140
_log.severe(
@@ -137,4 +148,86 @@ class DatabaseSeedingService {
137148
);
138149
}
139150
}
151+
152+
/// Seeds the database with global fixture data (categories, sources, etc.).
153+
///
154+
/// This method is idempotent, using `ON CONFLICT DO NOTHING` to prevent
155+
/// errors if the data already exists. It runs within a single transaction.
156+
Future<void> seedGlobalFixtureData() async {
157+
_log.info('Seeding global fixture data...');
158+
try {
159+
await _connection.execute('BEGIN');
160+
try {
161+
// Seed Categories
162+
_log.fine('Seeding categories...');
163+
for (final data in categoriesFixturesData) {
164+
final category = Category.fromJson(data);
165+
await _connection.execute(
166+
Sql.named(
167+
'INSERT INTO categories (id, name) VALUES (@id, @name) '
168+
'ON CONFLICT (id) DO NOTHING',
169+
),
170+
parameters: category.toJson(),
171+
);
172+
}
173+
174+
// Seed Sources
175+
_log.fine('Seeding sources...');
176+
for (final data in sourcesFixturesData) {
177+
final source = Source.fromJson(data);
178+
await _connection.execute(
179+
Sql.named(
180+
'INSERT INTO sources (id, name) VALUES (@id, @name) '
181+
'ON CONFLICT (id) DO NOTHING',
182+
),
183+
parameters: source.toJson(),
184+
);
185+
}
186+
187+
// Seed Countries
188+
_log.fine('Seeding countries...');
189+
for (final data in countriesFixturesData) {
190+
final country = Country.fromJson(data);
191+
await _connection.execute(
192+
Sql.named(
193+
'INSERT INTO countries (id, name, code) '
194+
'VALUES (@id, @name, @code) ON CONFLICT (id) DO NOTHING',
195+
),
196+
parameters: country.toJson(),
197+
);
198+
}
199+
200+
// Seed Headlines
201+
_log.fine('Seeding headlines...');
202+
for (final data in headlinesFixturesData) {
203+
final headline = Headline.fromJson(data);
204+
await _connection.execute(
205+
Sql.named(
206+
'INSERT INTO headlines (id, title, source_id, category_id, '
207+
'image_url, url, published_at, description, content) '
208+
'VALUES (@id, @title, @sourceId, @categoryId, @imageUrl, @url, '
209+
'@publishedAt, @description, @content) '
210+
'ON CONFLICT (id) DO NOTHING',
211+
),
212+
parameters: headline.toJson(),
213+
);
214+
}
215+
216+
await _connection.execute('COMMIT');
217+
_log.info('Global fixture data seeding completed successfully.');
218+
} catch (e) {
219+
await _connection.execute('ROLLBACK');
220+
rethrow;
221+
}
222+
} on Object catch (e, st) {
223+
_log.severe(
224+
'An error occurred during global fixture data seeding.',
225+
e,
226+
st,
227+
);
228+
throw OperationFailedException(
229+
'Failed to seed global fixture data: $e',
230+
);
231+
}
232+
}
140233
}

0 commit comments

Comments
 (0)