From 0656fd93909bf4205f486409a2a90ae6c9ba708f Mon Sep 17 00:00:00 2001 From: DatDang Date: Wed, 7 Aug 2024 11:17:59 +0700 Subject: [PATCH] TF-3040 Create `public_asset` feature --- .../datasource/public_asset_datasource.dart | 36 +++++ .../remote_public_asset_datasource_impl.dart | 70 +++++++++ .../data/network/public_asset_api.dart | 137 ++++++++++++++++++ .../public_asset_repository_impl.dart | 57 ++++++++ .../exceptions/public_asset_exceptions.dart | 15 ++ .../repository/public_asset_repository.dart | 34 +++++ 6 files changed, 349 insertions(+) create mode 100644 lib/features/public_asset/data/datasource/public_asset_datasource.dart create mode 100644 lib/features/public_asset/data/datasource_impl/remote_public_asset_datasource_impl.dart create mode 100644 lib/features/public_asset/data/network/public_asset_api.dart create mode 100644 lib/features/public_asset/data/repository/public_asset_repository_impl.dart create mode 100644 lib/features/public_asset/domain/exceptions/public_asset_exceptions.dart create mode 100644 lib/features/public_asset/domain/repository/public_asset_repository.dart diff --git a/lib/features/public_asset/data/datasource/public_asset_datasource.dart b/lib/features/public_asset/data/datasource/public_asset_datasource.dart new file mode 100644 index 000000000..98cff7fb5 --- /dev/null +++ b/lib/features/public_asset/data/datasource/public_asset_datasource.dart @@ -0,0 +1,36 @@ +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/public_asset.dart'; + +abstract class PublicAssetDatasource { + const PublicAssetDatasource(); + + Future> getPublicAssetFromIds( + Session session, + AccountId accountId, + {required List publicAssetIds} + ); + + Future createPublicAsset( + Session session, + AccountId accountId, + { + required Id blobId, + required IdentityId? identityId + } + ); + + Future deletePublicAssets( + Session session, + AccountId accountId, + {required List publicAssetIds} + ); + + Future updatePublicAssets( + Session session, + AccountId accountId, + {required List publicAssets} + ); +} \ No newline at end of file diff --git a/lib/features/public_asset/data/datasource_impl/remote_public_asset_datasource_impl.dart b/lib/features/public_asset/data/datasource_impl/remote_public_asset_datasource_impl.dart new file mode 100644 index 000000000..453554b37 --- /dev/null +++ b/lib/features/public_asset/data/datasource_impl/remote_public_asset_datasource_impl.dart @@ -0,0 +1,70 @@ +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/public_asset.dart'; +import 'package:tmail_ui_user/features/public_asset/data/datasource/public_asset_datasource.dart'; +import 'package:tmail_ui_user/features/public_asset/data/network/public_asset_api.dart'; +import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; + +class RemotePublicAssetDatasourceImpl implements PublicAssetDatasource { + const RemotePublicAssetDatasourceImpl(this._publicAssetApi, this._exceptionThrower); + + final PublicAssetApi _publicAssetApi; + final ExceptionThrower _exceptionThrower; + + @override + Future createPublicAsset( + Session session, + AccountId accountId, + { + required Id blobId, + required IdentityId? identityId + } + ) => Future.sync(() async { + return await _publicAssetApi.createPublicAsset( + session, + accountId, + blobId: blobId, + identityId: identityId); + }).catchError(_exceptionThrower.throwException); + + @override + Future deletePublicAssets( + Session session, + AccountId accountId, + {required List publicAssetIds} + ) => Future.sync(() async { + return await _publicAssetApi.destroyPublicAssets( + session, + accountId, + publicAssetIds: publicAssetIds + ); + }).catchError(_exceptionThrower.throwException); + + @override + Future> getPublicAssetFromIds( + Session session, + AccountId accountId, + {required List publicAssetIds} + ) => Future.sync(() async { + return await _publicAssetApi.getPublicAssets( + session, + accountId, + publicAssetIds: publicAssetIds + ); + }).catchError(_exceptionThrower.throwException); + + @override + Future updatePublicAssets( + Session session, + AccountId accountId, + {required List publicAssets} + ) => Future.sync(() async { + return await _publicAssetApi.updatePublicAssets( + session, + accountId, + publicAssets: publicAssets + ); + }).catchError(_exceptionThrower.throwException); +} \ No newline at end of file diff --git a/lib/features/public_asset/data/network/public_asset_api.dart b/lib/features/public_asset/data/network/public_asset_api.dart new file mode 100644 index 000000000..a533645d8 --- /dev/null +++ b/lib/features/public_asset/data/network/public_asset_api.dart @@ -0,0 +1,137 @@ +import 'package:jmap_dart_client/http/converter/identities/public_asset_identities_converter.dart'; +import 'package:jmap_dart_client/http/http_client.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/patch_object.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:jmap_dart_client/jmap/jmap_request.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/get/get_public_asset_method.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/get/get_public_asset_response.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/public_asset.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/set/set_public_asset_method.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/set/set_public_asset_response.dart'; +import 'package:tmail_ui_user/features/public_asset/domain/exceptions/public_asset_exceptions.dart'; +import 'package:uuid/uuid.dart'; + +class PublicAssetApi { + const PublicAssetApi(this._httpClient, this._uuid); + + final HttpClient _httpClient; + final Uuid _uuid; + + MapEntry _toPatchObjectMapEntry(PublicAsset publicAsset) { + final patchObject = PatchObject({ + PatchObject.identityIdsProperty: const PublicAssetIdentitiesConverter() + .toJson(publicAsset.identityIds!), + }); + + return MapEntry(publicAsset.id!, patchObject); + } + + Future> getPublicAssets( + Session session, + AccountId accountId, + {required List publicAssetIds} + ) async { + final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); + final method = GetPublicAssetMethod(accountId); + method.addIds(publicAssetIds.toSet()); + final invocation = requestBuilder.invocation(method); + final response = await (requestBuilder..usings(method.requiredCapabilities)) + .build() + .execute(); + final publicAssets = response.parse( + invocation.methodCallId, + GetPublicAssetResponse.deserialize + )?.list ?? []; + + return publicAssets; + } + + Future createPublicAsset( + Session session, + AccountId accountId, + { + required Id blobId, + required IdentityId? identityId + } + ) async { + final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); + final method = SetPublicAssetMethod(accountId); + final generateCreateId = Id(_uuid.v1()); + method.addCreate( + generateCreateId, + PublicAsset( + blobId: blobId, + identityIds: identityId != null ? {identityId: true} : {} + ) + ); + final invocation = requestBuilder.invocation(method); + final response = await (requestBuilder..usings(method.requiredCapabilities)) + .build() + .execute(); + final publicAsset = response.parse( + invocation.methodCallId, + SetPublicAssetResponse.deserialize + )?.created?[generateCreateId]; + + if (publicAsset == null) { + throw const CannotCreatePublicAssetException(); + } + + return publicAsset; + } + + Future destroyPublicAssets( + Session session, + AccountId accountId, + {required List publicAssetIds} + ) async { + final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); + final method = SetPublicAssetMethod(accountId); + method.addDestroy(publicAssetIds.toSet()); + final invocation = requestBuilder.invocation(method); + final response = await (requestBuilder..usings(method.requiredCapabilities)) + .build() + .execute(); + + final notDestroyedPublicAssetIds = response.parse( + invocation.methodCallId, + SetPublicAssetResponse.deserialize + )?.notDestroyed; + + if (notDestroyedPublicAssetIds?.isNotEmpty == true) { + throw const CannotDestroyPublicAssetException(); + } + } + + Future updatePublicAssets( + Session session, + AccountId accountId, + {required List publicAssets} + ) async { + final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); + final method = SetPublicAssetMethod(accountId); + method.addUpdates( + Map.fromEntries( + publicAssets + .where((publicAsset) => publicAsset.id != null && publicAsset.identityIds != null) + .map(_toPatchObjectMapEntry) + ) + ); + final invocation = requestBuilder.invocation(method); + final response = await (requestBuilder..usings(method.requiredCapabilities)) + .build() + .execute(); + + final notUpdatedPublicAssetIds = response.parse( + invocation.methodCallId, + SetPublicAssetResponse.deserialize + )?.updated; + + if (notUpdatedPublicAssetIds?.isNotEmpty == true) { + throw const CannotUpdatePublicAssetException(); + } + } +} \ No newline at end of file diff --git a/lib/features/public_asset/data/repository/public_asset_repository_impl.dart b/lib/features/public_asset/data/repository/public_asset_repository_impl.dart new file mode 100644 index 000000000..a113aad75 --- /dev/null +++ b/lib/features/public_asset/data/repository/public_asset_repository_impl.dart @@ -0,0 +1,57 @@ +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/public_asset.dart'; +import 'package:tmail_ui_user/features/public_asset/data/datasource/public_asset_datasource.dart'; +import 'package:tmail_ui_user/features/public_asset/domain/repository/public_asset_repository.dart'; + +class PublicAssetRepositoryImpl implements PublicAssetRepository { + const PublicAssetRepositoryImpl(this._publicAssetDatasource); + + final PublicAssetDatasource _publicAssetDatasource; + + @override + Future> getPublicAssetsFromIds( + Session session, + AccountId accountId, + {required List publicAssetIds} + ) => _publicAssetDatasource.getPublicAssetFromIds( + session, + accountId, + publicAssetIds: publicAssetIds); + + @override + Future deletePublicAssets( + Session session, + AccountId accountId, + {required List publicAssetIds} + ) => _publicAssetDatasource.deletePublicAssets( + session, + accountId, + publicAssetIds: publicAssetIds); + + @override + Future createPublicAsset( + Session session, + AccountId accountId, + { + required Id blobId, + required IdentityId? identityId + } + ) => _publicAssetDatasource.createPublicAsset( + session, + accountId, + blobId: blobId, + identityId: identityId); + + @override + Future updatePublicAssets( + Session session, + AccountId accountId, + {required List publicAssets} + ) => _publicAssetDatasource.updatePublicAssets( + session, + accountId, + publicAssets: publicAssets); +} \ No newline at end of file diff --git a/lib/features/public_asset/domain/exceptions/public_asset_exceptions.dart b/lib/features/public_asset/domain/exceptions/public_asset_exceptions.dart new file mode 100644 index 000000000..80a4906b9 --- /dev/null +++ b/lib/features/public_asset/domain/exceptions/public_asset_exceptions.dart @@ -0,0 +1,15 @@ +class UnsupportedCapabilityException implements Exception { + const UnsupportedCapabilityException(); +} + +class CannotCreatePublicAssetException implements Exception { + const CannotCreatePublicAssetException(); +} + +class CannotDestroyPublicAssetException implements Exception { + const CannotDestroyPublicAssetException(); +} + +class CannotUpdatePublicAssetException implements Exception { + const CannotUpdatePublicAssetException(); +} \ No newline at end of file diff --git a/lib/features/public_asset/domain/repository/public_asset_repository.dart b/lib/features/public_asset/domain/repository/public_asset_repository.dart new file mode 100644 index 000000000..dc2729ddb --- /dev/null +++ b/lib/features/public_asset/domain/repository/public_asset_repository.dart @@ -0,0 +1,34 @@ +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/public_asset.dart'; + +abstract class PublicAssetRepository { + Future> getPublicAssetsFromIds( + Session session, + AccountId accountId, + {required List publicAssetIds} + ); + + Future createPublicAsset( + Session session, + AccountId accountId, + { + required Id blobId, + required IdentityId? identityId + } + ); + + Future deletePublicAssets( + Session session, + AccountId accountId, + {required List publicAssetIds} + ); + + Future updatePublicAssets( + Session session, + AccountId accountId, + {required List publicAssets} + ); +} \ No newline at end of file