Skip to content

Commit

Permalink
Merge pull request #1749 from ardriveapp/PE-5911-enhance-logs-on-uplo…
Browse files Browse the repository at this point in the history
…ad-folders-and-files-string-upload-failed-upload-errors-unknown

PE-5911: feat(upload)
  • Loading branch information
thiagocarvalhodev authored May 29, 2024
2 parents 3a4fc07 + 1bda339 commit c129a1d
Show file tree
Hide file tree
Showing 19 changed files with 1,384 additions and 829 deletions.
21 changes: 14 additions & 7 deletions lib/blocs/upload/upload_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -607,11 +607,18 @@ class UploadCubit extends Cubit<UploadState> {
);

uploadController.onError((tasks) {
logger.e('Error uploading folders. Number of tasks: ${tasks.length}');
addError(Exception('Error uploading'));
logger.i('Error uploading folders. Number of tasks: ${tasks.length}');
emit(UploadFailure(
error: UploadErrors.unknown,
failedTasks: tasks,
controller: uploadController));
hasEmittedError = true;
});

uploadController.onFailedTask((task) {
logger.e('UploadTask failed. Task: ${task.errorInfo()}', task.error);
});

uploadController.onProgressChange(
(progress) {
emit(
Expand Down Expand Up @@ -651,8 +658,6 @@ class UploadCubit extends Cubit<UploadState> {

void retryUploads() {
if (state is UploadFailure) {
logger.i('Retrying uploads');

final controller = (state as UploadFailure).controller!;

controller.retryFailedTasks(_auth.currentUser.wallet);
Expand Down Expand Up @@ -722,7 +727,7 @@ class UploadCubit extends Cubit<UploadState> {
);

uploadController.onError((tasks) {
logger.e('Error uploading files. Number of tasks: ${tasks.length}');
logger.i('Error uploading files. Number of tasks: ${tasks.length}');
hasEmittedError = true;
emit(
UploadFailure(
Expand All @@ -733,10 +738,13 @@ class UploadCubit extends Cubit<UploadState> {
);
});

uploadController.onFailedTask((task) {
logger.e('UploadTask failed. Task: ${task.errorInfo()}', task.error);
});

uploadController.onProgressChange(
(progress) async {
// TODO: Save as the file is finished the upload

emit(
UploadInProgressUsingNewUploader(
progress: progress,
Expand Down Expand Up @@ -961,7 +969,6 @@ class UploadCubit extends Cubit<UploadState> {
}

emit(UploadFailure(error: UploadErrors.unknown));
logger.e('Failed to upload file', error, stackTrace);
super.onError(error, stackTrace);
}

Expand Down
1 change: 0 additions & 1 deletion lib/components/upload_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,6 @@ class _UploadFormState extends State<UploadForm> {
],
);
} else if (state is UploadFailure) {
logger.e('Upload failed: ${state.error}');
if (state.error == UploadErrors.turboTimeout) {
return ArDriveStandardModal(
title: appLocalizationsOf(context).uploadFailed,
Expand Down
1 change: 1 addition & 0 deletions packages/ardrive_uploader/lib/ardrive_uploader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export 'src/factories.dart';
export 'src/metadata_generator.dart';
export 'src/upload_controller.dart';
export 'src/upload_strategy.dart';
export 'src/upload_task.dart';
17 changes: 14 additions & 3 deletions packages/ardrive_uploader/lib/src/ardrive_uploader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';

import 'package:ardrive_io/ardrive_io.dart';
import 'package:ardrive_uploader/ardrive_uploader.dart';
import 'package:ardrive_uploader/src/upload_dispatcher.dart';
import 'package:ardrive_uploader/src/utils/logger.dart';
import 'package:ardrive_utils/ardrive_utils.dart';
import 'package:arweave/arweave.dart';
Expand Down Expand Up @@ -287,9 +288,19 @@ class _ArDriveUploader implements ArDriveUploader {
}

if (folderUploadTask != null) {
// first sends the upload task for the folder and then uploads the files
uploadController.sendTask(folderUploadTask, wallet, onTaskCompleted: () {
uploadController.sendTasks(wallet);
/// first sends the upload task for the folder and then uploads the files
uploadController.sendTask(folderUploadTask, wallet,
onTaskCompleted: (success) {
logger.i('Folder upload task completed with success: $success');
if (success) {
uploadController.sendTasks(wallet);
} else {
/// if the folder upload task fails, then all the files are marked as failed
for (var task in uploadController.notCompletedTasks) {
task = task.copyWith(status: UploadStatus.failed);
uploadController.updateProgress(task: task);
}
}
});
} else {
uploadController.sendTasks(wallet);
Expand Down
204 changes: 159 additions & 45 deletions packages/ardrive_uploader/lib/src/arfs_upload_metadata.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import 'package:ardrive_utils/ardrive_utils.dart';
import 'package:arweave/arweave.dart';

abstract class UploadMetadata {}

class ARFSDriveUploadMetadata extends ARFSUploadMetadata {
ARFSDriveUploadMetadata({
required super.entityMetadataTags,
required super.name,
required super.id,
required super.isPrivate,
required super.dataItemTags,
required super.bundleTags,
});

// TODO: implement toJson
Expand All @@ -19,21 +17,25 @@ class ARFSDriveUploadMetadata extends ARFSUploadMetadata {
}
}

/// Metadata for uploading a folder following the ARFS spec.
///
/// This metadata is used to create the metadata transaction for the folder.
class ARFSFolderUploadMetatadata extends ARFSUploadMetadata {
/// The ID of the drive where the folder will be uploaded.
final String driveId;

/// The ID of the parent folder where the folder will be uploaded.
final String? parentFolderId;

ARFSFolderUploadMetatadata({
required this.driveId,
this.parentFolderId,
required super.entityMetadataTags,
required super.name,
required super.id,
required super.isPrivate,
required super.dataItemTags,
required super.bundleTags,
});

/// Converts the metadata into a JSON object used on the metadata transaction.
@override
Map<String, dynamic> toJson() {
return {
Expand All @@ -42,15 +44,61 @@ class ARFSFolderUploadMetatadata extends ARFSUploadMetadata {
}
}

class ARFSFileUploadMetadata extends ARFSUploadMetadata {
final int size;
final DateTime lastModifiedDate;
final String dataContentType;
final String driveId;
final String parentFolderId;
final String? licenseDefinitionTxId;
final Map<String, String>? licenseAdditionalTags;
/// A mixin that provides data-related functionality for uploading files.
mixin ARFSUploadData {
/// The data tags for the file. These are the tags that will be added to the data transaction.
List<Tag> _dataTags = [];

/// The transaction ID for the data.
String? _dataTxId;

/// Gets the data transaction ID.
String? get dataTxId => _dataTxId;

/// Updates the data transaction ID.
void updateDataTxId(String dataTxId) {
_dataTxId = dataTxId;
}

/// The cipher tags for the data transaction.
Tag? _dataCipherTag;
Tag? _dataCipherIvTag;

// Method to set data cipher tags
void setDataCipher({
required String cipher,
required String cipherIv,
}) {
_dataCipherTag = Tag(
EntityTag.cipher,
cipher,
);
_dataCipherIvTag = Tag(
EntityTag.cipherIv,
cipherIv,
);
}

// Setter for data tags
void setDataTags(List<Tag> dataTags) => _dataTags = dataTags;

/// Gets the data tags including cipher tags if set.
///
/// Only call this method after setting the data tags and cipher tags if needed.
List<Tag> getDataTags() {
return [
..._dataTags,
if (_dataCipherTag != null) _dataCipherTag!,
if (_dataCipherIvTag != null) _dataCipherIvTag!,
];
}
}

/// Metadata for uploading a file following the ARFS spec.
///
/// This metadata is used to create the metadata transaction for the file.
/// It also contains the data tags that will be added to the data transaction.
class ARFSFileUploadMetadata extends ARFSUploadMetadata with ARFSUploadData {
ARFSFileUploadMetadata({
required this.size,
required this.lastModifiedDate,
Expand All @@ -59,63 +107,129 @@ class ARFSFileUploadMetadata extends ARFSUploadMetadata {
required this.parentFolderId,
this.licenseDefinitionTxId,
this.licenseAdditionalTags,
required super.entityMetadataTags,
required super.name,
required super.id,
required super.isPrivate,
required super.dataItemTags,
required super.bundleTags,
});

String? _dataTxId;
/// The size of the file in bytes.
final int size;

set setDataTxId(String dataTxId) => _dataTxId = dataTxId;
/// The last modified date of the file.
final DateTime lastModifiedDate;

String? get dataTxId => _dataTxId;
/// The content type of the file. e.g. 'image/jpeg'.
final String dataContentType;

String? _licenseTxId;
/// The ID of the drive where the file will be uploaded.
final String driveId;

/// The ID of the parent folder where the file will be uploaded.
final String parentFolderId;

set setLicenseTxId(String licenseTxId) => _licenseTxId = licenseTxId;
/// The transaction ID of the license definition.
final String? licenseDefinitionTxId;

/// Additional tags for the license.
final Map<String, String>? licenseAdditionalTags;

/// The transaction ID of the license transaction.
String? _licenseTxId;

// Getter for licenseTxId
String? get licenseTxId => _licenseTxId;

// Public method to set licenseTxId with validation or additional logic
void updateLicenseTxId(String licenseTxId) {
_licenseTxId = licenseTxId;
}

@override
Map<String, dynamic> toJson() => {
'name': name,
'size': size,
'lastModifiedDate': lastModifiedDate.millisecondsSinceEpoch,
'dataContentType': dataContentType,
'dataTxId': dataTxId,
}..addAll(licenseTxId != null
? {
'licenseTxId': licenseTxId,
}
: {});
Map<String, dynamic> toJson() {
if (dataTxId == null) {
throw StateError('dataTxId is required but not set.');
}

return {
'name': name,
'size': size,
'lastModifiedDate': lastModifiedDate.millisecondsSinceEpoch,
'dataContentType': dataContentType,
'dataTxId': dataTxId,
if (licenseTxId != null) 'licenseTxId': licenseTxId,
};
}
}

/// An abstract class that serves as a base for ARFS upload metadata.
///
/// This class provides common properties and methods for handling metadata
/// related to ARFS uploads, including the name, ID, and privacy status of the
/// entity, as well as methods for setting and retrieving entity metadata tags
/// and cipher tags.
abstract class ARFSUploadMetadata extends UploadMetadata {
final String id;
final String name;
final List<Tag> entityMetadataTags;
final List<Tag> dataItemTags;
final List<Tag> bundleTags;
final bool isPrivate;
String? _metadataTxId;

ARFSUploadMetadata({
required this.name,
required this.entityMetadataTags,
required this.dataItemTags,
required this.bundleTags,
required this.id,
required this.isPrivate,
});

/// The unique identifier for the entity.
final String id;

/// The name of the entity.
final String name;

/// Boolean indicating if the entity is private.
final bool isPrivate;

/// The metadata transaction ID.
String? _metadataTxId;

/// List of entity metadata tags.
late List<Tag> _entityMetadataTags;

/// Tags for the cipher. It's null if the entity is not encrypted.
Tag? _cipherTag;
Tag? _cipherIvTag;

/// Gets the entity metadata tags including cipher tags if set.
List<Tag> getEntityMetadataTags() {
return [
..._entityMetadataTags,
if (_cipherTag != null) _cipherTag!,
if (_cipherIvTag != null) _cipherIvTag!,
];
}

/// Sets the entity metadata tags.
void setEntityMetadataTags(List<Tag> entityMetadataTags) =>
_entityMetadataTags = entityMetadataTags;

/// Sets the cipher and IV tags for the entity.
void setCipher({
required String cipher,
required String cipherIv,
}) {
_cipherTag = Tag(
EntityTag.cipher,
cipher,
);
_cipherIvTag = Tag(
EntityTag.cipherIv,
cipherIv,
);
}

/// Sets the metadata transaction ID.
set setMetadataTxId(String metadataTxId) => _metadataTxId = metadataTxId;

/// Gets the metadata transaction ID.
String? get metadataTxId => _metadataTxId;

/// Converts the metadata into a JSON object used on the metadata transaction.
Map<String, dynamic> toJson();

@override
String toString() => toJson().toString();
String toString() => 'ARFSUploadMetadata: $name';
}
Loading

0 comments on commit c129a1d

Please sign in to comment.