Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/features/redis/redis_connection_form.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:querya_desktop/shared/report_error.dart';
import 'package:querya_desktop/core/database/database_error_mapper.dart';
import 'package:flutter/widgets.dart';
import 'dart:async';

Expand Down Expand Up @@ -220,8 +222,8 @@ class _RedisConnectionFormContentState
if (mounted) {
_showTestResult(result.ok ? 'success' : (result.error ?? 'failed'));
}
} catch (e) {
if (mounted) _showTestResult('error: $e');
} catch (e, st) {
if (mounted) _showTestResult(failureMessage('error', e, where: 'RedisConnectionForm', stack: st, driver: DatabaseDriver.redis));
}
}

Expand Down
58 changes: 30 additions & 28 deletions lib/features/redis/redis_key_editor.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:querya_desktop/shared/report_error.dart';
import 'package:querya_desktop/core/database/database_error_mapper.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart' as material;
import 'package:querya_desktop/core/theme/querya_ui_colors.dart';
Expand Down Expand Up @@ -315,11 +317,11 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
}
if (!mounted) return;
setState(() => _loadingMore = false);
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() {
_loadingMore = false;
_error = 'Load failed: $e';
_error = failureMessage('Load failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis);
});
}
}
Expand All @@ -336,9 +338,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
if (!mounted) return;
setState(() => _success = 'Value saved');
_clearSuccessAfterDelay();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'Save failed: $e');
setState(() => _error = failureMessage('Save failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand Down Expand Up @@ -377,9 +379,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
});
_clearSuccessAfterDelay();
widget.onKeyRenamed?.call(newBulk);
} catch (e) {
} catch (e, st) {
if (mounted) {
setState(() => _error = 'Rename failed: $e');
setState(() => _error = failureMessage('Rename failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}
}
Expand All @@ -397,9 +399,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
try {
await widget.connection.del(_cmdKey);
widget.onKeyDeleted?.call();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'Delete failed: $e');
setState(() => _error = failureMessage('Delete failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand All @@ -417,9 +419,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
_success = seconds > 0 ? 'TTL set to $seconds seconds' : 'TTL removed';
});
_clearSuccessAfterDelay();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'TTL failed: $e');
setState(() => _error = failureMessage('TTL failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand All @@ -429,9 +431,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
try {
await widget.connection.hset(_cmdKey, field, value);
await _load();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'HSET failed: $e');
setState(() => _error = failureMessage('HSET failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand All @@ -448,9 +450,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
try {
await widget.connection.hdel(_cmdKey, field);
await _load();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'HDEL failed: $e');
setState(() => _error = failureMessage('HDEL failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand All @@ -460,9 +462,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
try {
await widget.connection.rpush(_cmdKey, value);
await _load();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'RPUSH failed: $e');
setState(() => _error = failureMessage('RPUSH failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand All @@ -485,9 +487,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
}
await widget.connection.lset(_cmdKey, index, newValue);
await _load();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'LSET failed: $e');
setState(() => _error = failureMessage('LSET failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand Down Expand Up @@ -522,9 +524,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
rethrow;
}
await _load();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'LREM failed: $e');
setState(() => _error = failureMessage('LREM failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand All @@ -541,9 +543,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
try {
await widget.connection.sadd(_cmdKey, member);
await _load();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'SADD failed: $e');
setState(() => _error = failureMessage('SADD failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand All @@ -560,9 +562,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
try {
await widget.connection.srem(_cmdKey, member);
await _load();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'SREM failed: $e');
setState(() => _error = failureMessage('SREM failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand All @@ -572,9 +574,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
try {
await widget.connection.zadd(_cmdKey, score, member);
await _load();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'ZADD failed: $e');
setState(() => _error = failureMessage('ZADD failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand All @@ -591,9 +593,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
try {
await widget.connection.zrem(_cmdKey, member);
await _load();
} catch (e) {
} catch (e, st) {
if (!mounted) return;
setState(() => _error = 'ZREM failed: $e');
setState(() => _error = failureMessage('ZREM failed', e, where: 'RedisKeyEditor', stack: st, driver: DatabaseDriver.redis));
}
}

Expand Down
14 changes: 8 additions & 6 deletions lib/features/redis/redis_keys_view.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:querya_desktop/shared/report_error.dart';
import 'package:querya_desktop/core/database/database_error_mapper.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart' as material;
import 'package:querya_desktop/core/theme/querya_ui_colors.dart';
Expand Down Expand Up @@ -130,13 +132,13 @@ class _RedisKeysViewState extends material.State<RedisKeysView> {
ttl: metas[i].ttl,
),
];
} catch (e) {
} catch (e, st) {
// Still show keys with unknown type/TTL; surface the failure non-blocking.
infos = [
for (final name in accumulatedKeys)
RedisKeyInfo(name: name, type: 'unknown', ttl: -1),
];
typeTtlError = 'Failed to load key types/TTLs: $e';
typeTtlError = failureMessage('Failed to load key types/TTLs', e, where: 'RedisKeysView', stack: st, driver: DatabaseDriver.redis);
}
} else {
infos = [];
Expand Down Expand Up @@ -193,8 +195,8 @@ class _RedisKeysViewState extends material.State<RedisKeysView> {
_keys.removeWhere((k) => k.name == keyInfo.name);
_dbSize = (_dbSize - 1).clamp(0, _dbSize);
});
} catch (e) {
if (mounted) setState(() => _error = 'Delete failed: $e');
} catch (e, st) {
if (mounted) setState(() => _error = failureMessage('Delete failed', e, where: 'RedisKeysView', stack: st, driver: DatabaseDriver.redis));
}
}

Expand Down Expand Up @@ -222,11 +224,11 @@ class _RedisKeysViewState extends material.State<RedisKeysView> {
_keys.removeWhere((k) => toRemove.contains(k.name));
_dbSize = (_dbSize - count).clamp(0, _dbSize);
});
} catch (e) {
} catch (e, st) {
if (!mounted) return;
// Some chunks may have been removed: show what is really left.
await _load();
if (mounted) setState(() => _error = 'Delete folder failed: $e');
if (mounted) setState(() => _error = failureMessage('Delete folder failed', e, where: 'RedisKeysView', stack: st, driver: DatabaseDriver.redis));
}
}

Expand Down
14 changes: 14 additions & 0 deletions lib/shared/report_error.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,17 @@ void reportError(
message: '$action. ${describeError(error, driver: driver)}',
);
}

/// Logs [error] and returns `<action>: <reason>` for an inline error line (a
/// view's own error text), where a toast is not the surface. [action] is a
/// short phrase (`'Delete failed'`); [where] names the place for the log.
String failureMessage(
String action,
Object error, {
required String where,
StackTrace? stack,
DatabaseDriver? driver,
}) {
QueryaLog.instance.error(where, action, error: error, stack: stack);
return '$action: ${describeError(error, driver: driver)}';
}
15 changes: 15 additions & 0 deletions test/shared/report_error_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ void main() {
expect(text, contains('Cannot reach the database server'));
});

test('failureMessage logs and returns the inline error line', () async {
final text = failureMessage(
'Delete failed',
StateError('connection reset'),
where: 'RedisKeysView',
driver: DatabaseDriver.redis,
);
await QueryaLog.instance.flush();

expect(text, startsWith('Delete failed: '));
final log =
await File('${logDir.path}/${QueryaLog.fileName}').readAsString();
expect(log, contains('[RedisKeysView] Delete failed'));
});

testWidgets('reportError shows the toast and writes the log', (tester) async {
final host = await pumpHost(tester);

Expand Down
Loading