Skip to content

Commit

Permalink
Merge pull request #1797 from ardriveapp/PE-6464-could-not-roll-back-…
Browse files Browse the repository at this point in the history
…exception-could-not-roll-back-exception-error-bad-parameter-or-other-api-misuse

PE-6464: enhance error handling and logging
  • Loading branch information
thiagocarvalhodev authored Jul 22, 2024
2 parents 71d8d2d + 51c3585 commit e42f207
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 39 deletions.
5 changes: 5 additions & 0 deletions lib/authentication/login/views/login_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ class _LoginPageState extends State<LoginPage> {
});
}

if (loginState is LoginSuccess) {
logger.setContext(logger.context
.copyWith(userAddress: loginState.user.walletAddress));
}

if (loginState is PromptPassword) {
showEnterYourPasswordDialog(
context: context,
Expand Down
145 changes: 106 additions & 39 deletions lib/models/daos/drive_dao/drive_dao.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import 'dart:convert';

import 'package:ardrive/core/crypto/crypto.dart';
import 'package:ardrive/entities/entities.dart';
import 'package:ardrive/models/daos/drive_dao/exception.dart';
import 'package:ardrive/models/license.dart';
import 'package:ardrive/models/models.dart';
import 'package:ardrive/search/search_result.dart';
import 'package:ardrive/utils/logger.dart';
import 'package:ardrive_utils/ardrive_utils.dart';
import 'package:arweave/arweave.dart';
import 'package:cryptography/cryptography.dart';
Expand Down Expand Up @@ -50,102 +52,154 @@ class DriveDao extends DatabaseAccessor<Database> with _$DriveDaoMixin {
}

Future<void> deleteSharedPrivateDrives(String? owner) async {
final drives = (await allDrives().get()).where(
(drive) =>
drive.ownerAddress != owner &&
drive.privacy == DrivePrivacyTag.private,
);
for (var drive in drives) {
await detachDrive(drive.id);
try {
final drives = (await allDrives().get()).where(
(drive) =>
drive.ownerAddress != owner &&
drive.privacy == DrivePrivacyTag.private,
);
for (var drive in drives) {
await detachDrive(drive.id);
}
} catch (e) {
logger.e('Error deleting shared private drives', e);
}
}

Future<void> detachDrive(String driveId) async {
return db.transaction(() async {
await deleteDriveById(driveId: driveId);
await deleteAllDriveRevisionsByDriveId(driveId: driveId);
await deleteFoldersByDriveId(driveId: driveId);
await deleteFolderRevisionsByDriveId(driveId: driveId);
await deleteFilesForDriveId(driveId: driveId);
await deleteFileRevisionsByDriveId(driveId: driveId);
await deleteLicensesByDriveId(driveId: driveId);
});
try {
return db.transaction(() async {
await deleteDriveById(driveId: driveId);
await deleteAllDriveRevisionsByDriveId(driveId: driveId);
await deleteFoldersByDriveId(driveId: driveId);
await deleteFolderRevisionsByDriveId(driveId: driveId);
await deleteFilesForDriveId(driveId: driveId);
await deleteFileRevisionsByDriveId(driveId: driveId);
await deleteLicensesByDriveId(driveId: driveId);
});
} catch (e) {
throw _handleError('Error detaching drive', e);
}
}

Future<SecretKey?> getDriveKeyFromMemory(DriveID driveID) async {
return await _driveKeyVault.get(driveID);
try {
return await _driveKeyVault.get(driveID);
} catch (e) {
throw _handleError('Error getting drive key from memory', e);
}
}

Future<void> putDriveKeyInMemory({
required DriveID driveID,
required SecretKey driveKey,
}) async {
return await _driveKeyVault.put(driveID, driveKey);
try {
return await _driveKeyVault.put(driveID, driveKey);
} catch (e) {
throw _handleError('Error putting drive key in memory', e);
}
}

Future<Uint8List?> getPreviewDataFromMemory(TxID dataTxId) async {
return await _previewVault.get(dataTxId);
try {
return await _previewVault.get(dataTxId);
} catch (e) {
throw _handleError('Error getting preview data from memory', e);
}
}

Future<void> putPreviewDataInMemory({
required TxID dataTxId,
required Uint8List bytes,
}) async {
return await _previewVault.put(dataTxId, bytes);
try {
await _previewVault.put(dataTxId, bytes);
} catch (e) {
throw _handleError('Error putting preview data in memory', e);
}
}

Future<void> insertNewDriveRevisions(
List<DriveRevisionsCompanion> revisions,
) async {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.driveRevisions, revisions);
});
try {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.driveRevisions, revisions);
});
} catch (e) {
throw _handleError('Error inserting new drive revisions', e);
}
}

Future<void> insertNewFileRevisions(
List<FileRevisionsCompanion> revisions,
) async {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.fileRevisions, revisions);
});
try {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.fileRevisions, revisions);
});
} catch (e) {
throw _handleError('Error inserting new file revisions', e);
}
}

Future<void> insertNewFolderRevisions(
List<FolderRevisionsCompanion> revisions,
) async {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.folderRevisions, revisions);
});
try {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.folderRevisions, revisions);
});
} catch (e) {
throw _handleError('Error inserting new folder revisions', e);
}
}

Future<void> insertNewNetworkTransactions(
List<NetworkTransactionsCompanion> transactions,
) async {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.networkTransactions, transactions);
});
try {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.networkTransactions, transactions);
});
} catch (e) {
throw _handleError('Error inserting new network transactions', e);
}
}

Future<void> updateFolderEntries(
List<FolderEntriesCompanion> entries,
) async {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.folderEntries, entries);
});
try {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.folderEntries, entries);
});
} catch (e) {
throw _handleError('Error updating folder entries', e);
}
}

Future<void> updateFileEntries(
List<FileEntriesCompanion> entries,
) async {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.fileEntries, entries);
});
try {
await db.batch((b) async {
b.insertAllOnConflictUpdate(db.fileEntries, entries);
});
} catch (e) {
throw _handleError('Error updating file entries', e);
}
}

Future<void> updateDrive(
DrivesCompanion drive,
) async {
await (db.update(drives)..whereSamePrimaryKey(drive)).write(drive);
try {
await (db.update(drives)..whereSamePrimaryKey(drive)).write(drive);
} catch (e) {
throw _handleError('Error updating drive', e);
}
}

Future<void> runTransaction(
Expand All @@ -154,6 +208,11 @@ class DriveDao extends DatabaseAccessor<Database> with _$DriveDaoMixin {
await db.transaction(transaction);
}

DriveDAOException _handleError(String description, Object error) {
logger.i(description);
return DriveDAOException(message: description, error: error);
}

/// Creates a drive with its accompanying root folder.
Future<CreateDriveResult> createDrive({
required String name,
Expand Down Expand Up @@ -660,6 +719,14 @@ class DriveDao extends DatabaseAccessor<Database> with _$DriveDaoMixin {
await delete(networkTransactions).go();
});
}

Future<int> numberOfFiles() {
return (select(fileEntries).table.count()).getSingle();
}

Future<int> numberOfFolders() {
return (select(folderEntries).table.count()).getSingle();
}
}

class FolderNotFoundInDriveException implements Exception {
Expand Down
11 changes: 11 additions & 0 deletions lib/models/daos/drive_dao/exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class DriveDAOException implements Exception {
final String message;
final Object? error;

DriveDAOException({required this.message, this.error});

@override
String toString() {
return 'DriveDAOException: $message. Error: ${error.toString()}';
}
}
22 changes: 22 additions & 0 deletions lib/sync/domain/cubit/sync_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,31 @@ class SyncCubit extends Cubit<SyncState> {
);

_promptToSnapshotBloc.add(const SyncRunning(isRunning: false));

unawaited(_updateContext());

emit(SyncIdle());
}

Future<void> _updateContext() async {
try {
var context = logger.context;

final numberOfFiles = await _syncRepository.numberOfFilesInWallet();
final numberOfFolders = await _syncRepository.numberOfFoldersInWallet();

logger.setContext(
context.copyWith(
numberOfDrives: _syncProgress.drivesCount,
numberOfFiles: numberOfFiles,
numberOfFolders: numberOfFolders,
),
);
} catch (e) {
logger.w('Error setting context after sync');
}
}

int calculateSyncLastBlockHeight(int lastBlockHeight) {
if (_lastSync != null) {
return lastBlockHeight;
Expand Down
13 changes: 13 additions & 0 deletions lib/sync/domain/repositories/sync_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ abstract class SyncRepository {

Future<int> getCurrentBlockHeight();

Future<int> numberOfFilesInWallet();
Future<int> numberOfFoldersInWallet();

factory SyncRepository({
required ArweaveService arweave,
required DriveDao driveDao,
Expand Down Expand Up @@ -1043,6 +1046,16 @@ class _SyncRepository implements SyncRepository {

return latestRevisions.values.toList();
}

@override
Future<int> numberOfFilesInWallet() {
return _driveDao.numberOfFiles();
}

@override
Future<int> numberOfFoldersInWallet() {
return _driveDao.numberOfFolders();
}
}

const fetchPhaseWeight = 0.1;
Expand Down
37 changes: 37 additions & 0 deletions packages/ardrive_logger/lib/src/ardrive_context.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
class ArDriveContext {
ArDriveContext({
this.userAddress,
this.numberOfDrives,
this.numberOfFiles,
this.numberOfFolders,
});

final String? userAddress;

final int? numberOfDrives;
final int? numberOfFiles;
final int? numberOfFolders;

// TODO: Add more context variables
// final bool? isArconnect;
// final bool? isMetamask;

ArDriveContext copyWith({
String? userAddress,
int? numberOfDrives,
int? numberOfFiles,
int? numberOfFolders,
}) {
return ArDriveContext(
userAddress: userAddress ?? this.userAddress,
numberOfDrives: numberOfDrives ?? this.numberOfDrives,
numberOfFiles: numberOfFiles ?? this.numberOfFiles,
numberOfFolders: numberOfFolders ?? this.numberOfFolders,
);
}

@override
String toString() {
return 'ArDriveContext(userAddress: $userAddress, numberOfDrives: $numberOfDrives, numberOfFiles: $numberOfFiles, numberOfFolders: $numberOfFolders)';
}
}
12 changes: 12 additions & 0 deletions packages/ardrive_logger/lib/src/logger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'dart:convert';

import 'package:ardrive_io/ardrive_io.dart';
import 'package:ardrive_logger/ardrive_logger.dart';
import 'package:ardrive_logger/src/ardrive_context.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_email_sender/flutter_email_sender.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
Expand Down Expand Up @@ -44,6 +45,8 @@ class Logger {
final LogExporter _logExporter;
late ListQueue<String> inMemoryLogs;

ArDriveContext _context = ArDriveContext();

/// A null value means that all errors will be logged to Sentry.
final ShouldLogErrorCallback? _shouldLogErrorCallback;

Expand Down Expand Up @@ -92,6 +95,15 @@ class Logger {
Sentry.captureException(error ?? message, stackTrace: stackTrace);
}

ArDriveContext get context => _context;

void setContext(ArDriveContext context) {
_context = context;
debugPrint('Context updated: ${_context.toString()}');

Sentry.addBreadcrumb(Breadcrumb(message: _context.toString()));
}

void log(LogLevel level, String message) {
final shouldLog = _shouldLog(level);
final shouldSaveInMemory = _shouldSaveInMemory(level);
Expand Down

0 comments on commit e42f207

Please sign in to comment.