From 96ae6d497dffb7f0d16f9b0064293c5052c0e6b8 Mon Sep 17 00:00:00 2001 From: DatDang Date: Mon, 5 Aug 2024 15:50:42 +0700 Subject: [PATCH 1/3] Implement get and set methods of PublicAsset extension --- .../public_asset_identities_converter.dart | 25 ++ .../capability/capability_identifier.dart | 1 + lib/jmap/core/error/set_error.dart | 1 + lib/jmap/core/patch_object.dart | 1 + .../get/get_public_asset_method.dart | 29 ++ .../get/get_public_asset_method.g.dart | 31 ++ .../get/get_public_asset_response.dart | 22 + .../get/get_public_asset_response.g.dart | 29 ++ .../extensions/public_asset/public_asset.dart | 48 +++ .../public_asset/public_asset.g.dart | 52 +++ .../set/set_public_asset_method.dart | 46 ++ .../set/set_public_asset_response.dart | 51 +++ .../get/get_public_asset_method_test.dart | 145 +++++++ .../set/set_public_asset_method_test.dart | 396 ++++++++++++++++++ 14 files changed, 877 insertions(+) create mode 100644 lib/http/converter/identities/public_asset_identities_converter.dart create mode 100644 lib/jmap/mail/extensions/public_asset/get/get_public_asset_method.dart create mode 100644 lib/jmap/mail/extensions/public_asset/get/get_public_asset_method.g.dart create mode 100644 lib/jmap/mail/extensions/public_asset/get/get_public_asset_response.dart create mode 100644 lib/jmap/mail/extensions/public_asset/get/get_public_asset_response.g.dart create mode 100644 lib/jmap/mail/extensions/public_asset/public_asset.dart create mode 100644 lib/jmap/mail/extensions/public_asset/public_asset.g.dart create mode 100644 lib/jmap/mail/extensions/public_asset/set/set_public_asset_method.dart create mode 100644 lib/jmap/mail/extensions/public_asset/set/set_public_asset_response.dart create mode 100644 test/jmap/mail/extensions/public_asset/get/get_public_asset_method_test.dart create mode 100644 test/jmap/mail/extensions/public_asset/set/set_public_asset_method_test.dart diff --git a/lib/http/converter/identities/public_asset_identities_converter.dart b/lib/http/converter/identities/public_asset_identities_converter.dart new file mode 100644 index 0000000..b6413e2 --- /dev/null +++ b/lib/http/converter/identities/public_asset_identities_converter.dart @@ -0,0 +1,25 @@ +import 'package:jmap_dart_client/http/converter/identities/identity_id_converter.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:json_annotation/json_annotation.dart'; + +class PublicAssetIdentitiesConverter extends JsonConverter, Map> { + const PublicAssetIdentitiesConverter(); + + @override + Map fromJson(Map json) { + return Map.fromEntries( + json.entries.map( + (entry) => MapEntry( + const IdentityIdConverter().fromJson(entry.key), + entry.value))); + } + + @override + Map toJson(Map object) { + return Map.fromEntries( + object.entries.map( + (entry) => MapEntry( + const IdentityIdConverter().toJson(entry.key), + entry.value))); + } +} \ No newline at end of file diff --git a/lib/jmap/core/capability/capability_identifier.dart b/lib/jmap/core/capability/capability_identifier.dart index a8399e7..2113a4b 100644 --- a/lib/jmap/core/capability/capability_identifier.dart +++ b/lib/jmap/core/capability/capability_identifier.dart @@ -11,6 +11,7 @@ class CapabilityIdentifier with EquatableMixin { static final jmapTeamMailboxes = CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:shares')); static final jamesSortOrder = CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:identity:sortorder')); static final jamesCalendarEvent = CapabilityIdentifier(Uri.parse('com:linagora:params:calendar:event')); + static final jmapPublicAsset = CapabilityIdentifier(Uri.parse('com:linagora:params:jmap:public:assets')); final Uri value; diff --git a/lib/jmap/core/error/set_error.dart b/lib/jmap/core/error/set_error.dart index 6c3ff9e..d88f094 100644 --- a/lib/jmap/core/error/set_error.dart +++ b/lib/jmap/core/error/set_error.dart @@ -15,6 +15,7 @@ class SetError with EquatableMixin { static final invalidPatch = ErrorType("invalidPatch"); static final willDestroy = ErrorType("willDestroy"); static final invalidProperties = ErrorType("invalidProperties"); + static final invalidArguments = ErrorType("invalidArguments"); static final singleton = ErrorType("singleton"); final ErrorType type; diff --git a/lib/jmap/core/patch_object.dart b/lib/jmap/core/patch_object.dart index 47cc850..48a0ec4 100644 --- a/lib/jmap/core/patch_object.dart +++ b/lib/jmap/core/patch_object.dart @@ -4,6 +4,7 @@ class PatchObject with EquatableMixin { static final mailboxIdsProperty = 'mailboxIds'; static final keywordsProperty = 'keywords'; + static const identityIdsProperty = 'identityIds'; PatchObject(this.patches); diff --git a/lib/jmap/mail/extensions/public_asset/get/get_public_asset_method.dart b/lib/jmap/mail/extensions/public_asset/get/get_public_asset_method.dart new file mode 100644 index 0000000..1240c1f --- /dev/null +++ b/lib/jmap/mail/extensions/public_asset/get/get_public_asset_method.dart @@ -0,0 +1,29 @@ +import 'package:jmap_dart_client/http/converter/account_id_converter.dart'; +import 'package:jmap_dart_client/http/converter/id_converter.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/method/method.dart'; +import 'package:jmap_dart_client/jmap/core/method/request/get_method.dart'; +import 'package:jmap_dart_client/jmap/core/request/request_invocation.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'get_public_asset_method.g.dart'; + +@JsonSerializable(converters: [AccountIdConverter(), IdConverter()]) +class GetPublicAssetMethod extends MethodRequiringAccountId with OptionalIds { + GetPublicAssetMethod(super.accountId); + + @override + MethodName get methodName => MethodName('PublicAsset/get'); + + @override + List get props => [accountId, ids]; + + @override + Set get requiredCapabilities => { + CapabilityIdentifier.jmapCore, + CapabilityIdentifier.jmapPublicAsset + }; + + @override + Map toJson() => _$GetPublicAssetMethodToJson(this); +} \ No newline at end of file diff --git a/lib/jmap/mail/extensions/public_asset/get/get_public_asset_method.g.dart b/lib/jmap/mail/extensions/public_asset/get/get_public_asset_method.g.dart new file mode 100644 index 0000000..fb3e394 --- /dev/null +++ b/lib/jmap/mail/extensions/public_asset/get/get_public_asset_method.g.dart @@ -0,0 +1,31 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'get_public_asset_method.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GetPublicAssetMethod _$GetPublicAssetMethodFromJson( + Map json) => + GetPublicAssetMethod( + const AccountIdConverter().fromJson(json['accountId'] as String), + )..ids = (json['ids'] as List?) + ?.map((e) => const IdConverter().fromJson(e as String)) + .toSet(); + +Map _$GetPublicAssetMethodToJson( + GetPublicAssetMethod instance) { + final val = { + 'accountId': const AccountIdConverter().toJson(instance.accountId), + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('ids', instance.ids?.map(const IdConverter().toJson).toList()); + return val; +} diff --git a/lib/jmap/mail/extensions/public_asset/get/get_public_asset_response.dart b/lib/jmap/mail/extensions/public_asset/get/get_public_asset_response.dart new file mode 100644 index 0000000..92df4c9 --- /dev/null +++ b/lib/jmap/mail/extensions/public_asset/get/get_public_asset_response.dart @@ -0,0 +1,22 @@ +import 'package:jmap_dart_client/http/converter/account_id_converter.dart'; +import 'package:jmap_dart_client/http/converter/id_converter.dart'; +import 'package:jmap_dart_client/http/converter/state_converter.dart'; +import 'package:jmap_dart_client/jmap/core/method/response/get_response.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/public_asset.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'get_public_asset_response.g.dart'; + +@StateConverter() +@AccountIdConverter() +@IdConverter() +@JsonSerializable() +class GetPublicAssetResponse extends GetResponse { + GetPublicAssetResponse(super.accountId, super.state, super.list, super.notFound); + + @override + List get props => [accountId, state, list, notFound]; + + static GetPublicAssetResponse deserialize(Map json) + => _$GetPublicAssetResponseFromJson(json); +} \ No newline at end of file diff --git a/lib/jmap/mail/extensions/public_asset/get/get_public_asset_response.g.dart b/lib/jmap/mail/extensions/public_asset/get/get_public_asset_response.g.dart new file mode 100644 index 0000000..40997aa --- /dev/null +++ b/lib/jmap/mail/extensions/public_asset/get/get_public_asset_response.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'get_public_asset_response.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GetPublicAssetResponse _$GetPublicAssetResponseFromJson( + Map json) => + GetPublicAssetResponse( + const AccountIdConverter().fromJson(json['accountId'] as String), + const StateConverter().fromJson(json['state'] as String), + (json['list'] as List) + .map((e) => PublicAsset.fromJson(e as Map)) + .toList(), + (json['notFound'] as List?) + ?.map((e) => const IdConverter().fromJson(e as String)) + .toList(), + ); + +Map _$GetPublicAssetResponseToJson( + GetPublicAssetResponse instance) => + { + 'accountId': const AccountIdConverter().toJson(instance.accountId), + 'state': const StateConverter().toJson(instance.state), + 'list': instance.list, + 'notFound': instance.notFound?.map(const IdConverter().toJson).toList(), + }; diff --git a/lib/jmap/mail/extensions/public_asset/public_asset.dart b/lib/jmap/mail/extensions/public_asset/public_asset.dart new file mode 100644 index 0000000..7ca9622 --- /dev/null +++ b/lib/jmap/mail/extensions/public_asset/public_asset.dart @@ -0,0 +1,48 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/http/converter/id_nullable_converter.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/identities/identity.dart'; +import 'package:jmap_dart_client/http/converter/identities/public_asset_identities_converter.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'public_asset.g.dart'; + +typedef PublicAssetIdentities = Map; + +@JsonSerializable( + converters: [ + IdNullableConverter(), + PublicAssetIdentitiesConverter(), + ], + includeIfNull: false, +) +class PublicAsset with EquatableMixin { + final Id? id; + final String? publicURI; + final int? size; + final String? contentType; + final Id? blobId; + final PublicAssetIdentities? identityIds; + + PublicAsset({ + this.id, + this.publicURI, + this.size, + this.contentType, + this.blobId, + this.identityIds, + }); + + @override + List get props => [ + id, + publicURI, + size, + contentType, + blobId, + identityIds, + ]; + + factory PublicAsset.fromJson(Map json) => _$PublicAssetFromJson(json); + Map toJson() => _$PublicAssetToJson(this); +} diff --git a/lib/jmap/mail/extensions/public_asset/public_asset.g.dart b/lib/jmap/mail/extensions/public_asset/public_asset.g.dart new file mode 100644 index 0000000..8f073a0 --- /dev/null +++ b/lib/jmap/mail/extensions/public_asset/public_asset.g.dart @@ -0,0 +1,52 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'public_asset.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PublicAsset _$PublicAssetFromJson(Map json) => PublicAsset( + id: const IdNullableConverter().fromJson(json['id'] as String?), + publicURI: json['publicURI'] as String?, + size: json['size'] as int?, + contentType: json['contentType'] as String?, + blobId: const IdNullableConverter().fromJson(json['blobId'] as String?), + identityIds: + _$JsonConverterFromJson, Map>( + json['identityIds'], + const PublicAssetIdentitiesConverter().fromJson), + ); + +Map _$PublicAssetToJson(PublicAsset instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('id', const IdNullableConverter().toJson(instance.id)); + writeNotNull('publicURI', instance.publicURI); + writeNotNull('size', instance.size); + writeNotNull('contentType', instance.contentType); + writeNotNull('blobId', const IdNullableConverter().toJson(instance.blobId)); + writeNotNull( + 'identityIds', + _$JsonConverterToJson, Map>( + instance.identityIds, const PublicAssetIdentitiesConverter().toJson)); + return val; +} + +Value? _$JsonConverterFromJson( + Object? json, + Value? Function(Json json) fromJson, +) => + json == null ? null : fromJson(json as Json); + +Json? _$JsonConverterToJson( + Value? value, + Json? Function(Value value) toJson, +) => + value == null ? null : toJson(value); diff --git a/lib/jmap/mail/extensions/public_asset/set/set_public_asset_method.dart b/lib/jmap/mail/extensions/public_asset/set/set_public_asset_method.dart new file mode 100644 index 0000000..b9cb3d8 --- /dev/null +++ b/lib/jmap/mail/extensions/public_asset/set/set_public_asset_method.dart @@ -0,0 +1,46 @@ +import 'package:jmap_dart_client/http/converter/account_id_converter.dart'; +import 'package:jmap_dart_client/http/converter/id_converter.dart'; +import 'package:jmap_dart_client/http/converter/set/set_method_properties_converter.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/method/request/set_method.dart'; +import 'package:jmap_dart_client/jmap/core/request/request_invocation.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/public_asset.dart'; + +class SetPublicAssetMethod extends SetMethod { + + SetPublicAssetMethod(super.accountId); + + @override + MethodName get methodName => MethodName('PublicAsset/set'); + + @override + List get props => [accountId, ifInState, create, update, destroy]; + + @override + Set get requiredCapabilities => { + CapabilityIdentifier.jmapCore, + CapabilityIdentifier.jmapPublicAsset + }; + + @override + Map toJson() { + final val = { + 'accountId': const AccountIdConverter().toJson(accountId), + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('create', create + ?.map((id, create) => SetMethodPropertiesConverter().fromMapIdToJson(id, create.toJson()))); + writeNotNull('update', update + ?.map((id, update) => SetMethodPropertiesConverter().fromMapIdToJson(id, update.toJson()))); + writeNotNull('destroy', destroy + ?.map((destroyId) => const IdConverter().toJson(destroyId)).toList()); + + return val; + } +} \ No newline at end of file diff --git a/lib/jmap/mail/extensions/public_asset/set/set_public_asset_response.dart b/lib/jmap/mail/extensions/public_asset/set/set_public_asset_response.dart new file mode 100644 index 0000000..5ac04fc --- /dev/null +++ b/lib/jmap/mail/extensions/public_asset/set/set_public_asset_response.dart @@ -0,0 +1,51 @@ +import 'package:jmap_dart_client/http/converter/account_id_converter.dart'; +import 'package:jmap_dart_client/http/converter/id_converter.dart'; +import 'package:jmap_dart_client/http/converter/state_nullable_converter.dart'; +import 'package:jmap_dart_client/jmap/core/error/set_error.dart'; +import 'package:jmap_dart_client/jmap/core/method/response/set_response.dart'; +import 'package:jmap_dart_client/jmap/mail/extensions/public_asset/public_asset.dart'; + +class SetPublicAssetResponse extends SetResponse { + SetPublicAssetResponse( + super.accountId, { + super.newState, + super.created, + super.updated, + super.destroyed, + super.notCreated, + super.notUpdated, + super.notDestroyed, + }); + + @override + List get props => [accountId, newState, created, updated, destroyed]; + + static SetPublicAssetResponse deserialize(Map json) { + return SetPublicAssetResponse( + const AccountIdConverter().fromJson(json['accountId'] as String), + newState: const StateNullableConverter().fromJson(json['newState'] as String?), + created: (json['created'] as Map?) + ?.map((key, value) => MapEntry( + const IdConverter().fromJson(key), + PublicAsset.fromJson(value as Map))), + updated: (json['updated'] as Map?) + ?.map((key, value) => MapEntry( + const IdConverter().fromJson(key), + value != null ? PublicAsset.fromJson(value as Map) : null)), + destroyed: (json['destroyed'] as List?) + ?.map((id) => const IdConverter().fromJson(id)).toSet(), + notCreated: (json['notCreated'] as Map?) + ?.map((key, value) => MapEntry( + const IdConverter().fromJson(key), + SetError.fromJson(value))), + notUpdated: (json['notUpdated'] as Map?) + ?.map((key, value) => MapEntry( + const IdConverter().fromJson(key), + SetError.fromJson(value))), + notDestroyed: (json['notDestroyed'] as Map?) + ?.map((key, value) => MapEntry( + const IdConverter().fromJson(key), + SetError.fromJson(value))), + ); + } +} \ No newline at end of file diff --git a/test/jmap/mail/extensions/public_asset/get/get_public_asset_method_test.dart b/test/jmap/mail/extensions/public_asset/get/get_public_asset_method_test.dart new file mode 100644 index 0000000..28063c5 --- /dev/null +++ b/test/jmap/mail/extensions/public_asset/get/get_public_asset_method_test.dart @@ -0,0 +1,145 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:http_mock_adapter/http_mock_adapter.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/request/request_invocation.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'; + +void main() { + final baseOption = BaseOptions(method: 'POST'); + final dio = Dio(baseOption)..options.baseUrl = 'http://domain.com/jmap'; + final dioAdapter = DioAdapter(dio: dio); + final dioAdapterHeaders = {"accept": "application/json;jmapVersion=rfc-8621"}; + final httpClient = HttpClient(dio); + final processingInvocation = ProcessingInvocation(); + final requestBuilder = JmapRequestBuilder(httpClient, processingInvocation); + final accountId = AccountId(Id('123abc')); + final identityId = IdentityId(Id('some-identity-id')); + final methodCallId = MethodCallId('c0'); + final publicAsset = PublicAsset( + id: Id('abc123'), + blobId: Id('def456'), + size: 123, + contentType: 'image/jpeg', + publicURI: 'http://domain.com/public/abc123', + identityIds: {identityId: true} + ); + + group('get public asset method test:', () { + test( + 'should return expected PublicAsset ' + 'when call method PublicAsset/get with exist public asset id', + () async { + // arrange + final getPublicAssetMethod = GetPublicAssetMethod(accountId)..addIds({publicAsset.id!}); + final invocation = requestBuilder.invocation(getPublicAssetMethod, methodCallId: methodCallId); + dioAdapter.onPost( + '', + (server) => server.reply( + 200, + { + "sessionState": "abcdefghij", + "methodResponses": [[ + getPublicAssetMethod.methodName.value, + { + "accountId": accountId.id.value, + "state": 'some-state', + "list": [publicAsset.toJson()], + "notFound": [], + }, + methodCallId.value + ]] + } + ), + data: { + "using": getPublicAssetMethod.requiredCapabilities + .map((capability) => capability.value.toString()) + .toList(), + "methodCalls": [ + [ + getPublicAssetMethod.methodName.value, + { + "accountId": accountId.id.value, + "ids": [publicAsset.id?.value], + }, + methodCallId.value + ] + ] + }, + headers: dioAdapterHeaders, + ); + + // act + final response = (await (requestBuilder..usings(getPublicAssetMethod.requiredCapabilities)) + .build() + .execute()) + .parse( + invocation.methodCallId, + GetPublicAssetResponse.deserialize); + + // assert + expect((response)?.list, equals([publicAsset])); + }); + + test( + 'should return public asset id in notFound ' + 'when call method PublicAsset/get with non-exist public asset id', + () async { + // arrange + final getPublicAssetMethod = GetPublicAssetMethod(accountId)..addIds({publicAsset.id!}); + final invocation = requestBuilder.invocation(getPublicAssetMethod, methodCallId: methodCallId); + dioAdapter.onPost( + '', + (server) => server.reply( + 200, + { + "sessionState": "abcdefghij", + "methodResponses": [[ + getPublicAssetMethod.methodName.value, + { + "accountId": accountId.id.value, + "state": 'some-state', + "list": [], + "notFound": [publicAsset.id?.value], + }, + methodCallId.value + ]] + } + ), + data: { + "using": getPublicAssetMethod.requiredCapabilities + .map((capability) => capability.value.toString()) + .toList(), + "methodCalls": [ + [ + getPublicAssetMethod.methodName.value, + { + "accountId": accountId.id.value, + "ids": [publicAsset.id?.value], + }, + methodCallId.value + ] + ] + }, + headers: dioAdapterHeaders, + ); + + // act + final response = (await (requestBuilder..usings(getPublicAssetMethod.requiredCapabilities)) + .build() + .execute()) + .parse( + invocation.methodCallId, + GetPublicAssetResponse.deserialize); + + // assert + expect((response)?.notFound, equals([publicAsset.id])); + }); + }); +} \ No newline at end of file diff --git a/test/jmap/mail/extensions/public_asset/set/set_public_asset_method_test.dart b/test/jmap/mail/extensions/public_asset/set/set_public_asset_method_test.dart new file mode 100644 index 0000000..f8cb077 --- /dev/null +++ b/test/jmap/mail/extensions/public_asset/set/set_public_asset_method_test.dart @@ -0,0 +1,396 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:http_mock_adapter/http_mock_adapter.dart'; +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/error/set_error.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/request/request_invocation.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/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'; + +void main() { + final baseOption = BaseOptions(method: 'POST'); + final dio = Dio(baseOption)..options.baseUrl = 'http://domain.com/jmap'; + final dioAdapter = DioAdapter(dio: dio); + final dioAdapterHeaders = {"accept": "application/json;jmapVersion=rfc-8621"}; + final httpClient = HttpClient(dio); + final processingInvocation = ProcessingInvocation(); + final requestBuilder = JmapRequestBuilder(httpClient, processingInvocation); + final accountId = AccountId(Id('123abc')); + final identityId = IdentityId(Id('some-identity-id')); + final methodCallId = MethodCallId('c0'); + final publicAsset = PublicAsset( + id: Id('abc123'), + blobId: Id('def456'), + size: 123, + contentType: 'image/jpeg', + publicURI: 'http://domain.com/public/abc123', + identityIds: {identityId: true} + ); + + group('set public asset method test:', () { + test( + 'should return created public asset ' + 'when PublicAsset/set create return success', + () async { + // arrange + final createId = Id('def456'); + final createObject = PublicAsset( + blobId: publicAsset.blobId, + identityIds: publicAsset.identityIds); + final method = SetPublicAssetMethod(accountId) + ..addCreate(createId, createObject); + final invocation = requestBuilder.invocation(method, methodCallId: methodCallId); + dioAdapter.onPost( + '', + (server) => server.reply(200, { + "sessionState": "abcdefghij", + "methodResponses": [[ + method.methodName.value, + { + "accountId": accountId.id.value, + "newState": 'some-state', + "created": {createId.value: publicAsset.toJson()}, + }, + methodCallId.value + ]] + }), + data: { + "using": method.requiredCapabilities + .map((capability) => capability.value.toString()) + .toList(), + "methodCalls": [ + [ + method.methodName.value, + { + "accountId": accountId.id.value, + "create": {createId.value: createObject.toJson()}, + }, + methodCallId.value + ], + ] + }, + headers: dioAdapterHeaders, + ); + + // act + final response = (await (requestBuilder..usings(method.requiredCapabilities)) + .build() + .execute()) + .parse( + invocation.methodCallId, + SetPublicAssetResponse.deserialize); + + // assert + expect(response?.created, equals({createId: publicAsset})); + }); + + test( + 'should return notCreated ' + 'when PublicAsset/set create return failure', + () async { + // arrange + const errorDescription = 'Missing \'/blobId\' property'; + final createId = Id('def456'); + final createObject = PublicAsset( + blobId: null, + identityIds: publicAsset.identityIds); + final method = SetPublicAssetMethod(accountId) + ..addCreate(createId, createObject); + final invocation = requestBuilder.invocation(method, methodCallId: methodCallId); + dioAdapter.onPost( + '', + (server) => server.reply(200, { + "sessionState": "abcdefghij", + "methodResponses": [[ + method.methodName.value, + { + "accountId": accountId.id.value, + "oldState": 'some-old-state', + "newState": 'some-state', + "notCreated": { + createId.value: { + "type": "invalidArguments", + "description": errorDescription + } + } + }, + methodCallId.value + ]] + }), + data: { + "using": method.requiredCapabilities + .map((capability) => capability.value.toString()) + .toList(), + "methodCalls": [ + [ + method.methodName.value, + { + "accountId": accountId.id.value, + "create": {createId.value: createObject.toJson()}, + }, + methodCallId.value + ], + ] + }, + headers: dioAdapterHeaders, + ); + + // act + final response = (await (requestBuilder..usings(method.requiredCapabilities)) + .build() + .execute()) + .parse( + invocation.methodCallId, + SetPublicAssetResponse.deserialize); + + // assert + expect( + response?.notCreated?[createId], + SetError(SetError.invalidArguments, description: errorDescription), + ); + }); + + test( + 'should return destroyed public asset ids ' + 'when PublicAsset/set destroy return success', + () async { + // arrange + final method = SetPublicAssetMethod(accountId) + ..addDestroy({publicAsset.id!}); + final invocation = requestBuilder.invocation(method, methodCallId: methodCallId); + dioAdapter.onPost( + '', + (server) => server.reply(200, { + "sessionState": "abcdefghij", + "methodResponses": [[ + method.methodName.value, + { + "accountId": accountId.id.value, + "newState": 'some-state', + "destroyed": [publicAsset.id?.value], + }, + methodCallId.value + ]] + }), + data: { + "using": method.requiredCapabilities + .map((capability) => capability.value.toString()) + .toList(), + "methodCalls": [ + [ + method.methodName.value, + { + "accountId": accountId.id.value, + "destroy": [publicAsset.id?.value], + }, + methodCallId.value + ], + ] + }, + headers: dioAdapterHeaders, + ); + + // act + final response = (await (requestBuilder..usings(method.requiredCapabilities)) + .build() + .execute()) + .parse( + invocation.methodCallId, + SetPublicAssetResponse.deserialize); + + // assert + expect(response?.destroyed, equals({publicAsset.id})); + }); + + test( + 'should return notDestroyed ' + 'when PublicAsset/set destroy return failure', + () async { + // arrange + String errorDescription(String? id) => 'Invalid UUID string: $id'; + final method = SetPublicAssetMethod(accountId) + ..addDestroy({publicAsset.id!}); + final invocation = requestBuilder.invocation(method, methodCallId: methodCallId); + dioAdapter.onPost( + '', + (server) => server.reply(200, { + "sessionState": "abcdefghij", + "methodResponses": [[ + method.methodName.value, + { + "accountId": accountId.id.value, + "oldState": 'some-old-state', + "newState": 'some-state', + "notDestroyed": { + publicAsset.id?.value: { + "type": "invalidArguments", + "description": errorDescription(publicAsset.id?.value) + } + } + }, + methodCallId.value + ]] + }), + data: { + "using": method.requiredCapabilities + .map((capability) => capability.value.toString()) + .toList(), + "methodCalls": [ + [ + method.methodName.value, + { + "accountId": accountId.id.value, + "destroy": [publicAsset.id?.value], + }, + methodCallId.value + ], + ] + }, + headers: dioAdapterHeaders, + ); + + // act + final response = (await (requestBuilder..usings(method.requiredCapabilities)) + .build() + .execute()) + .parse( + invocation.methodCallId, + SetPublicAssetResponse.deserialize); + + // assert + expect( + response?.notDestroyed?[publicAsset.id], + SetError(SetError.invalidArguments, description: errorDescription(publicAsset.id?.value)), + ); + }); + + test( + 'should return updated public asset ids ' + 'when PublicAsset/set update return success', + () async { + // arrange + final updateObject = PatchObject({ + PatchObject.identityIdsProperty: const PublicAssetIdentitiesConverter() + .toJson(publicAsset.identityIds!), + }); + final method = SetPublicAssetMethod(accountId) + ..addUpdates({publicAsset.id!: updateObject}); + final invocation = requestBuilder.invocation(method, methodCallId: methodCallId); + dioAdapter.onPost( + '', + (server) => server.reply(200, { + "sessionState": "abcdefghij", + "methodResponses": [[ + method.methodName.value, + { + "accountId": accountId.id.value, + "newState": 'some-state', + "updated": {publicAsset.id?.value: null}, + }, + methodCallId.value + ]] + }), + data: { + "using": method.requiredCapabilities + .map((capability) => capability.value.toString()) + .toList(), + "methodCalls": [ + [ + method.methodName.value, + { + "accountId": accountId.id.value, + "update": {publicAsset.id?.value: updateObject.toJson()}, + }, + methodCallId.value + ], + ] + }, + headers: dioAdapterHeaders, + ); + + // act + final response = (await (requestBuilder..usings(method.requiredCapabilities)) + .build() + .execute()) + .parse( + invocation.methodCallId, + SetPublicAssetResponse.deserialize); + + // assert + expect(response?.updated, equals({publicAsset.id: null})); + }); + + test( + 'should return notUpdated ' + 'when PublicAsset/set update return failure', + () async { + // arrange + const errorDescription = 'Invalid identity'; + final updateObject = PatchObject({ + PatchObject.identityIdsProperty: const PublicAssetIdentitiesConverter() + .toJson(publicAsset.identityIds!), + }); + final method = SetPublicAssetMethod(accountId) + ..addUpdates({publicAsset.id!: updateObject}); + final invocation = requestBuilder.invocation(method, methodCallId: methodCallId); + dioAdapter.onPost( + '', + (server) => server.reply(200, { + "sessionState": "abcdefghij", + "methodResponses": [[ + method.methodName.value, + { + "accountId": accountId.id.value, + "oldState": 'some-old-state', + "newState": 'some-state', + "notUpdated": { + publicAsset.id?.value: { + "type": "invalidArguments", + "description": errorDescription + } + } + }, + methodCallId.value + ]] + }), + data: { + "using": method.requiredCapabilities + .map((capability) => capability.value.toString()) + .toList(), + "methodCalls": [ + [ + method.methodName.value, + { + "accountId": accountId.id.value, + "update": {publicAsset.id?.value: updateObject.toJson()}, + }, + methodCallId.value + ], + ] + }, + headers: dioAdapterHeaders, + ); + + // act + final response = (await (requestBuilder..usings(method.requiredCapabilities)) + .build() + .execute()) + .parse( + invocation.methodCallId, + SetPublicAssetResponse.deserialize); + + // assert + expect( + response?.notUpdated?[publicAsset.id], + SetError(SetError.invalidArguments, description: errorDescription), + ); + }); + }); +} \ No newline at end of file From 8dfee5c0386d3848e60673430ca7cb389fa61a54 Mon Sep 17 00:00:00 2001 From: Dat PHAM HOANG Date: Thu, 8 Aug 2024 14:38:35 +0700 Subject: [PATCH 2/3] Change events to trigger CI --- .github/workflows/ci.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab121d9..17ee23b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,15 +6,7 @@ env: FLUTTER_VERSION: 3.22.2 # Controls when the workflow will run -on: - # Triggers the workflow on push or pull request events but only for the master branch - push: - branches: [ main ] - pull_request: - branches: [ main ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: +on: [push, pull_request, workflow_dispatch] # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: From 204c97eaa69c73af2d7740bff18ba4308fe8693f Mon Sep 17 00:00:00 2001 From: DatDang Date: Tue, 20 Aug 2024 16:30:25 +0700 Subject: [PATCH 3/3] Add over quota error type --- lib/jmap/core/error/method/error_method_response.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/jmap/core/error/method/error_method_response.dart b/lib/jmap/core/error/method/error_method_response.dart index 5864d38..6542dab 100644 --- a/lib/jmap/core/error/method/error_method_response.dart +++ b/lib/jmap/core/error/method/error_method_response.dart @@ -15,6 +15,7 @@ abstract class ErrorMethodResponse extends MethodResponse { static final accountNotSupportedByMethod = ErrorType("accountNotSupportedByMethod"); static final accountReadOnly = ErrorType("accountReadOnly"); static final cannotCalculateChanges = ErrorType("cannotCalculateChanges"); + static final overQuota = ErrorType("overQuota"); final ErrorType type; final String? description;