From 03978f2b01488044f404e5a5609e93ce6b45afe7 Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 22 Dec 2022 15:04:23 +0700 Subject: [PATCH 1/3] Add new capability for Team Mailboxes --- lib/jmap/core/capability/capability_identifier.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/jmap/core/capability/capability_identifier.dart b/lib/jmap/core/capability/capability_identifier.dart index 437a911..e66c67b 100644 --- a/lib/jmap/core/capability/capability_identifier.dart +++ b/lib/jmap/core/capability/capability_identifier.dart @@ -8,6 +8,7 @@ class CapabilityIdentifier with EquatableMixin { static final jmapWebSocket = CapabilityIdentifier(Uri.parse('urn:ietf:params:jmap:websocket')); static final jmapMdn = CapabilityIdentifier(Uri.parse('urn:ietf:params:jmap:mdn')); static final jmapQuota = CapabilityIdentifier(Uri.parse('urn:ietf:params:jmap:quota')); + static final jmapTeamMailboxes = CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:shares')); final Uri value; From 7d0db09c277316a3027564fd2d015a2f7192b6ba Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 22 Dec 2022 15:05:44 +0700 Subject: [PATCH 2/3] Add property for mailbox to support team mailboxes --- .../converter/namespace_nullable_converter.dart | 12 ++++++++++++ lib/jmap/mail/mailbox/mailbox.dart | 13 ++++++++++++- lib/jmap/mail/mailbox/mailbox.g.dart | 9 +++++++++ lib/jmap/mail/mailbox/namespace.dart | 12 ++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 lib/http/converter/namespace_nullable_converter.dart create mode 100644 lib/jmap/mail/mailbox/namespace.dart diff --git a/lib/http/converter/namespace_nullable_converter.dart b/lib/http/converter/namespace_nullable_converter.dart new file mode 100644 index 0000000..c3bae5e --- /dev/null +++ b/lib/http/converter/namespace_nullable_converter.dart @@ -0,0 +1,12 @@ +import 'package:jmap_dart_client/jmap/mail/mailbox/namespace.dart'; +import 'package:json_annotation/json_annotation.dart'; + +class NamespaceNullableConverter implements JsonConverter { + const NamespaceNullableConverter(); + + @override + Namespace? fromJson(String? json) => json != null ? Namespace(json) : null; + + @override + String? toJson(Namespace? object) => object?.value; +} \ No newline at end of file diff --git a/lib/jmap/mail/mailbox/mailbox.dart b/lib/jmap/mail/mailbox/mailbox.dart index d6c5223..9e7fc06 100644 --- a/lib/jmap/mail/mailbox/mailbox.dart +++ b/lib/jmap/mail/mailbox/mailbox.dart @@ -2,6 +2,7 @@ import 'package:equatable/equatable.dart'; import 'package:jmap_dart_client/http/converter/is_subscribed_converter.dart'; import 'package:jmap_dart_client/http/converter/mailbox_id_nullable_converter.dart'; import 'package:jmap_dart_client/http/converter/mailbox_name_converter.dart'; +import 'package:jmap_dart_client/http/converter/namespace_nullable_converter.dart'; import 'package:jmap_dart_client/http/converter/role_converter.dart'; import 'package:jmap_dart_client/http/converter/sort_order_converter.dart'; import 'package:jmap_dart_client/http/converter/total_email_converter.dart'; @@ -12,10 +13,12 @@ import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox_rights.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/namespace.dart'; import 'package:json_annotation/json_annotation.dart'; part 'mailbox.g.dart'; +@NamespaceNullableConverter() @IsSubscribedConverter() @UnreadThreadsConverter() @UnreadEmailsConverter() @@ -65,6 +68,12 @@ class Mailbox with EquatableMixin { @JsonKey(includeIfNull: false) final IsSubscribed? isSubscribed; + @JsonKey(includeIfNull: false) + final Namespace? namespace; + + @JsonKey(includeIfNull: false) + final Map?>? rights; + Mailbox({ this.id, this.name, @@ -76,7 +85,9 @@ class Mailbox with EquatableMixin { this.totalThreads, this.unreadThreads, this.myRights, - this.isSubscribed + this.isSubscribed, + this.namespace, + this.rights }); factory Mailbox.fromJson(Map json) => _$MailboxFromJson(json); diff --git a/lib/jmap/mail/mailbox/mailbox.g.dart b/lib/jmap/mail/mailbox/mailbox.g.dart index c3d83a0..18887bb 100644 --- a/lib/jmap/mail/mailbox/mailbox.g.dart +++ b/lib/jmap/mail/mailbox/mailbox.g.dart @@ -26,6 +26,12 @@ Mailbox _$MailboxFromJson(Map json) => Mailbox( : MailboxRights.fromJson(json['myRights'] as Map), isSubscribed: const IsSubscribedConverter().fromJson(json['isSubscribed'] as bool?), + namespace: const NamespaceNullableConverter() + .fromJson(json['namespace'] as String?), + rights: (json['rights'] as Map?)?.map( + (k, e) => MapEntry( + k, (e as List?)?.map((e) => e as String).toList()), + ), ); Map _$MailboxToJson(Mailbox instance) { @@ -55,5 +61,8 @@ Map _$MailboxToJson(Mailbox instance) { writeNotNull('myRights', instance.myRights); writeNotNull('isSubscribed', const IsSubscribedConverter().toJson(instance.isSubscribed)); + writeNotNull('namespace', + const NamespaceNullableConverter().toJson(instance.namespace)); + writeNotNull('rights', instance.rights); return val; } diff --git a/lib/jmap/mail/mailbox/namespace.dart b/lib/jmap/mail/mailbox/namespace.dart new file mode 100644 index 0000000..ce98676 --- /dev/null +++ b/lib/jmap/mail/mailbox/namespace.dart @@ -0,0 +1,12 @@ + +import 'package:equatable/equatable.dart'; + +class Namespace with EquatableMixin { + + final String value; + + Namespace(this.value); + + @override + List get props => [value]; +} \ No newline at end of file From b8579d13a2fa4ff8ca9f05542cf23f3837d8f979 Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 22 Dec 2022 15:06:29 +0700 Subject: [PATCH 3/3] Add unit-test for get mailbox method --- .../mail/mailbox/get/get_mailbox_method.dart | 6 + .../mailbox/get/get_mailbox_method_test.dart | 258 ++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 test/jmap/mailbox/get/get_mailbox_method_test.dart diff --git a/lib/jmap/mail/mailbox/get/get_mailbox_method.dart b/lib/jmap/mail/mailbox/get/get_mailbox_method.dart index e147006..ae9fbd3 100644 --- a/lib/jmap/mail/mailbox/get/get_mailbox_method.dart +++ b/lib/jmap/mail/mailbox/get/get_mailbox_method.dart @@ -26,6 +26,12 @@ class GetMailboxMethod extends GetMethod { CapabilityIdentifier.jmapMail }; + Set get requiredCapabilitiesSupportTeamMailboxes => { + CapabilityIdentifier.jmapCore, + CapabilityIdentifier.jmapMail, + CapabilityIdentifier.jmapTeamMailboxes + }; + @override List get props => [methodName, accountId, ids, properties, requiredCapabilities]; diff --git a/test/jmap/mailbox/get/get_mailbox_method_test.dart b/test/jmap/mailbox/get/get_mailbox_method_test.dart new file mode 100644 index 0000000..8541b88 --- /dev/null +++ b/test/jmap/mailbox/get/get_mailbox_method_test.dart @@ -0,0 +1,258 @@ +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/unsigned_int.dart'; +import 'package:jmap_dart_client/jmap/jmap_request.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_method.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/get/get_mailbox_response.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox_rights.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/namespace.dart'; + +void main() { + group('test to json get mailbox method', () { + final expectedMailbox1 = Mailbox( + id: MailboxId(Id('f1cef2d0-30a9-11eb-9a8d-254ee97830fe')), + role: Role('inbox'), + name: MailboxName('INBOX'), + sortOrder: SortOrder(sortValue: 10), + totalEmails: TotalEmails(UnsignedInt(1847)), + unreadEmails: UnreadEmails(UnsignedInt(1708)), + totalThreads: TotalThreads(UnsignedInt(1847)), + unreadThreads: UnreadThreads(UnsignedInt(1708)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true, + ), + isSubscribed: IsSubscribed(false), + rights: { + "firstname23.surname23@upn.integration-open-paas.org": [ + "i", + "l", + "r", + "s", + "t", + "w" + ] + }, + namespace: Namespace('Personal') + ); + + final expectedMailbox2 = Mailbox( + id: MailboxId(Id('f1cef2d0-30a9-11eb-9a8d-254ee97830fe')), + role: Role('inbox'), + name: MailboxName('INBOX'), + sortOrder: SortOrder(sortValue: 10), + totalEmails: TotalEmails(UnsignedInt(1847)), + unreadEmails: UnreadEmails(UnsignedInt(1708)), + totalThreads: TotalThreads(UnsignedInt(1847)), + unreadThreads: UnreadThreads(UnsignedInt(1708)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true, + ), + isSubscribed: IsSubscribed(false) + ); + + test('get mailbox method and response parsing has team mailboxes', () async { + final baseOption = BaseOptions(method: 'POST'); + final dio = Dio(baseOption) + ..options.baseUrl = 'http://domain.com/jmap'; + final dioAdapter = DioAdapter(dio: dio); + dioAdapter.onPost( + '', + (server) => server.reply(200, { + "sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943", + "methodResponses": [ + [ + "Mailbox/get", + { + "accountId": "0d14dbabe6482aff5cbf922e04cef51a40b4eabccbe12d28fe27c97038752555", + "notFound": [ + + ], + "state": "c7b1bb10-80f5-11ed-b960-5773e4d60b2f", + "list": [ + { + "totalThreads": 1847, + "name": "INBOX", + "isSubscribed": false, + "role": "inbox", + "totalEmails": 1847, + "unreadThreads": 1708, + "unreadEmails": 1708, + "sortOrder": 10, + "rights": { + "firstname23.surname23@upn.integration-open-paas.org": [ + "i", + "l", + "r", + "s", + "t", + "w" + ] + }, + "namespace": "Personal", + "myRights": { + "mayReadItems": true, + "mayAddItems": true, + "mayRemoveItems": true, + "maySetSeen": true, + "maySetKeywords": true, + "mayCreateChild": true, + "mayRename": true, + "mayDelete": true, + "maySubmit": true + }, + "id": "f1cef2d0-30a9-11eb-9a8d-254ee97830fe" + } + ] + }, + "c0" + ] + ] + }), + data: { + "using": [ + "urn:ietf:params:jmap:core", + "urn:ietf:params:jmap:mail", + "urn:apache:james:params:jmap:mail:shares" + ], + "methodCalls": [ + [ + "Mailbox/get", + { + "accountId": "0d14dbabe6482aff5cbf922e04cef51a40b4eabccbe12d28fe27c97038752555" + }, + "c0" + ] + ] + }, + headers: { + "accept": "application/json;jmapVersion=rfc-8621", + "content-type": "application/json; charset=utf-8", + "content-length": 228 + } + ); + + final getMailboxMethod = GetMailboxMethod(AccountId(Id('0d14dbabe6482aff5cbf922e04cef51a40b4eabccbe12d28fe27c97038752555'))); + final httpClient = HttpClient(dio); + final requestBuilder = JmapRequestBuilder(httpClient, ProcessingInvocation()); + final getMailboxInvocation = requestBuilder.invocation(getMailboxMethod); + final response = await (requestBuilder + ..usings(getMailboxMethod.requiredCapabilitiesSupportTeamMailboxes)) + .build() + .execute(); + + final getMailboxResponse = response.parse( + getMailboxInvocation.methodCallId, + GetMailboxResponse.deserialize); + + expect(getMailboxResponse?.list.length, equals(1)); + expect(getMailboxResponse?.list, containsAll([expectedMailbox1])); + }); + + test('get mailbox method and response parsing not have team mailboxes', () async { + final baseOption = BaseOptions(method: 'POST'); + final dio = Dio(baseOption) + ..options.baseUrl = 'http://domain.com/jmap'; + final dioAdapter = DioAdapter(dio: dio); + dioAdapter.onPost( + '', + (server) => server.reply(200, { + "sessionState": "2c9f1b12-b35a-43e6-9af2-0106fb53a943", + "methodResponses": [ + [ + "Mailbox/get", + { + "accountId": "0d14dbabe6482aff5cbf922e04cef51a40b4eabccbe12d28fe27c97038752555", + "notFound": [ + + ], + "state": "c7b1bb10-80f5-11ed-b960-5773e4d60b2f", + "list": [ + { + "totalThreads": 1847, + "name": "INBOX", + "isSubscribed": false, + "role": "inbox", + "totalEmails": 1847, + "unreadThreads": 1708, + "unreadEmails": 1708, + "sortOrder": 10, + "myRights": { + "mayReadItems": true, + "mayAddItems": true, + "mayRemoveItems": true, + "maySetSeen": true, + "maySetKeywords": true, + "mayCreateChild": true, + "mayRename": true, + "mayDelete": true, + "maySubmit": true + }, + "id": "f1cef2d0-30a9-11eb-9a8d-254ee97830fe" + } + ] + }, + "c0" + ] + ] + }), + data: { + "using": [ + "urn:ietf:params:jmap:core", + "urn:ietf:params:jmap:mail" + ], + "methodCalls": [ + [ + "Mailbox/get", + { + "accountId": "0d14dbabe6482aff5cbf922e04cef51a40b4eabccbe12d28fe27c97038752555" + }, + "c0" + ] + ] + }, + headers: { + "accept": "application/json;jmapVersion=rfc-8621", + "content-type": "application/json; charset=utf-8", + "content-length": 185 + } + ); + + final getMailboxMethod = GetMailboxMethod(AccountId(Id('0d14dbabe6482aff5cbf922e04cef51a40b4eabccbe12d28fe27c97038752555'))); + final httpClient = HttpClient(dio); + final requestBuilder = JmapRequestBuilder(httpClient, ProcessingInvocation()); + final getMailboxInvocation = requestBuilder.invocation(getMailboxMethod); + final response = await (requestBuilder + ..usings(getMailboxMethod.requiredCapabilities)) + .build() + .execute(); + + final getMailboxResponse = response.parse( + getMailboxInvocation.methodCallId, + GetMailboxResponse.deserialize); + + expect(getMailboxResponse?.list.length, equals(1)); + expect(getMailboxResponse?.list, containsAll([expectedMailbox2])); + }); + }); +} \ No newline at end of file