diff --git a/lib/http/converter/account_converter.dart b/lib/http/converter/account_converter.dart new file mode 100644 index 0000000..d76e200 --- /dev/null +++ b/lib/http/converter/account_converter.dart @@ -0,0 +1,27 @@ +import 'package:jmap_dart_client/http/converter/account_name_converter.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/account/account.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; + +import 'capabilities_converter.dart'; + +class AccountConverter { + const AccountConverter(); + + MapEntry convert(String key, dynamic value, CapabilitiesConverter converter) { + final accountId = AccountId(Id(key)); + final account = accountFromJson(value, converter); + return MapEntry(accountId, account); + } + + Account accountFromJson(Map json, CapabilitiesConverter converter) { + return Account( + const AccountNameConverter().fromJson(json['name'] as String), + json['isPersonal'] as bool, + json['isReadOnly'] as bool, + (json['accountCapabilities'] as Map) + .map((key, value) => converter.convert(key, value)) + ); + } + +} \ No newline at end of file diff --git a/lib/http/converter/account_name_converter.dart b/lib/http/converter/account_name_converter.dart new file mode 100644 index 0000000..0f175d2 --- /dev/null +++ b/lib/http/converter/account_name_converter.dart @@ -0,0 +1,12 @@ +import 'package:jmap_dart_client/jmap/core/account/account.dart'; +import 'package:json_annotation/json_annotation.dart'; + +class AccountNameConverter implements JsonConverter { + const AccountNameConverter(); + + @override + AccountName fromJson(String json) => AccountName(json); + + @override + String toJson(AccountName object) => object.value; +} \ No newline at end of file diff --git a/lib/http/converter/capabilities_converter.dart b/lib/http/converter/capabilities_converter.dart new file mode 100644 index 0000000..510ac2a --- /dev/null +++ b/lib/http/converter/capabilities_converter.dart @@ -0,0 +1,54 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; +import 'package:jmap_dart_client/jmap/core/capability/core_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/default_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/mail_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/mdn_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/submission_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/vacation_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/websocket_capability.dart'; + +class CapabilitiesConverter { + static final defaultConverter = CapabilitiesConverter(); + + BuiltMap)>? mapCapabilitiesConverter; + final _mapCapabilityConverterBuilder = MapBuilder)>(); + + CapabilitiesConverter() { + _mapCapabilityConverterBuilder.addAll( + { + CapabilityIdentifier.jmapMail: MailCapability.deserialize, + CapabilityIdentifier.jmapCore: CoreCapability.deserialize, + CapabilityIdentifier.jmapSubmission: SubmissionCapability.deserialize, + CapabilityIdentifier.jmapVacationResponse: VacationCapability.deserialize, + CapabilityIdentifier.jmapWebSocket: WebSocketCapability.deserialize, + CapabilityIdentifier.jmapMdn: MdnCapability.deserialize + }); + } + + void addConverters(Map)> converters) { + _mapCapabilityConverterBuilder.addAll(converters); + } + + void build() { + mapCapabilitiesConverter = _mapCapabilityConverterBuilder.build(); + } + + BuiltMap? getConverters() { + return mapCapabilitiesConverter; + } + + MapEntry convert(String key, dynamic value) { + if (mapCapabilitiesConverter == null) { + build(); + } + + final identifier = CapabilityIdentifier(Uri.parse(key)); + if (mapCapabilitiesConverter!.containsKey(identifier)) { + return MapEntry(identifier, mapCapabilitiesConverter![identifier]!.call(value)); + } else { + return MapEntry(identifier, DefaultCapability(value)); + } + } +} \ No newline at end of file diff --git a/lib/http/converter/capability_identifier_onverter.dart b/lib/http/converter/capability_identifier_converter.dart similarity index 75% rename from lib/http/converter/capability_identifier_onverter.dart rename to lib/http/converter/capability_identifier_converter.dart index c4a1fdc..833171c 100644 --- a/lib/http/converter/capability_identifier_onverter.dart +++ b/lib/http/converter/capability_identifier_converter.dart @@ -1,12 +1,12 @@ -import 'package:jmap_dart_client/jmap/core/capability/capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:json_annotation/json_annotation.dart'; class CapabilityIdentifierConverter implements JsonConverter { const CapabilityIdentifierConverter(); @override - CapabilityIdentifier fromJson(String json) => CapabilityIdentifier(json); + CapabilityIdentifier fromJson(String json) => CapabilityIdentifier(Uri.parse(json)); @override - String toJson(CapabilityIdentifier object) => object.value; + String toJson(CapabilityIdentifier object) => object.value.toString(); } \ No newline at end of file diff --git a/lib/http/converter/unsigned_int_converter.dart b/lib/http/converter/unsigned_int_converter.dart new file mode 100644 index 0000000..6efad76 --- /dev/null +++ b/lib/http/converter/unsigned_int_converter.dart @@ -0,0 +1,16 @@ +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:json_annotation/json_annotation.dart'; + +class UnsignedIntConverter implements JsonConverter { + const UnsignedIntConverter(); + + @override + UnsignedInt fromJson(int json) { + return UnsignedInt(json); + } + + @override + int toJson(UnsignedInt object) { + return object.value.toInt(); + } +} \ No newline at end of file diff --git a/lib/http/converter/url_jmap_converter.dart b/lib/http/converter/url_jmap_converter.dart new file mode 100644 index 0000000..080d131 --- /dev/null +++ b/lib/http/converter/url_jmap_converter.dart @@ -0,0 +1,12 @@ +import 'package:jmap_dart_client/jmap/core/url_jmap.dart'; +import 'package:json_annotation/json_annotation.dart'; + +class UrlJmapConverter implements JsonConverter { + const UrlJmapConverter(); + + @override + UrlJmap fromJson(String json) => UrlJmap(json); + + @override + String toJson(UrlJmap object) => object.value; +} \ No newline at end of file diff --git a/lib/http/converter/user_name_converter.dart b/lib/http/converter/user_name_converter.dart new file mode 100644 index 0000000..2864153 --- /dev/null +++ b/lib/http/converter/user_name_converter.dart @@ -0,0 +1,12 @@ +import 'package:jmap_dart_client/jmap/core/user_name.dart'; +import 'package:json_annotation/json_annotation.dart'; + +class UserNameConverter implements JsonConverter { + const UserNameConverter(); + + @override + UserName fromJson(String json) => UserName(json); + + @override + String toJson(UserName object) => object.value; +} \ No newline at end of file diff --git a/lib/http/http_client.dart b/lib/http/http_client.dart index a10e95d..15fe97f 100644 --- a/lib/http/http_client.dart +++ b/lib/http/http_client.dart @@ -34,4 +34,24 @@ class HttpClient { .then((value) => value.data) .catchError((error) => throw error); } + + Future get( + String path, { + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + ProgressCallback? onReceiveProgress, + }) async { + final newOptions = options?.appendHeaders({HttpHeaders.acceptHeader : jmapHeader}) + ?? Options(headers: {HttpHeaders.acceptHeader : jmapHeader}) ; + + return await _dio.get( + path, + queryParameters: queryParameters, + options: newOptions, + cancelToken: cancelToken, + onReceiveProgress: onReceiveProgress) + .then((value) => value.data) + .catchError((error) => throw error); + } } \ No newline at end of file diff --git a/lib/jmap/core/account/account.dart b/lib/jmap/core/account/account.dart new file mode 100644 index 0000000..3e6c25a --- /dev/null +++ b/lib/jmap/core/account/account.dart @@ -0,0 +1,31 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; + +class Account with EquatableMixin { + + final AccountName name; + final bool isPersonal; + final bool isReadOnly; + final Map accountCapabilities; + + Account( + this.name, + this.isPersonal, + this.isReadOnly, + this.accountCapabilities, + ); + + @override + List get props => [name, isPersonal, isReadOnly, accountCapabilities]; +} + +class AccountName with EquatableMixin { + + final String value; + + AccountName(this.value); + + @override + List get props => [value]; +} diff --git a/lib/jmap/core/capability/capability.dart b/lib/jmap/core/capability/capability.dart deleted file mode 100644 index 140ecdf..0000000 --- a/lib/jmap/core/capability/capability.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:equatable/equatable.dart'; - -class CapabilityIdentifier with EquatableMixin { - static final jmapCore = CapabilityIdentifier('urn:ietf:params:jmap:core'); - static final jmapMail = CapabilityIdentifier('urn:ietf:params:jmap:mail'); - - final String value; - - CapabilityIdentifier(this.value); - - @override - List get props => [value]; -} - -class CapabilityProperties {} - -abstract class Capability { - final CapabilityIdentifier identifier; - final CapabilityProperties properties; - - Capability(this.identifier, this.properties); -} - - - diff --git a/lib/jmap/core/capability/capability_identifier.dart b/lib/jmap/core/capability/capability_identifier.dart new file mode 100644 index 0000000..656f4e1 --- /dev/null +++ b/lib/jmap/core/capability/capability_identifier.dart @@ -0,0 +1,17 @@ +import 'package:equatable/equatable.dart'; + +class CapabilityIdentifier with EquatableMixin { + static final jmapCore = CapabilityIdentifier(Uri.parse('urn:ietf:params:jmap:core')); + static final jmapMail = CapabilityIdentifier(Uri.parse('urn:ietf:params:jmap:mail')); + static final jmapSubmission = CapabilityIdentifier(Uri.parse('urn:ietf:params:jmap:submission')); + static final jmapVacationResponse = CapabilityIdentifier(Uri.parse('urn:ietf:params:jmap:vacationresponse')); + static final jmapWebSocket = CapabilityIdentifier(Uri.parse('urn:ietf:params:jmap:websocket')); + static final jmapMdn = CapabilityIdentifier(Uri.parse('urn:ietf:params:jmap:mdn')); + + final Uri value; + + CapabilityIdentifier(this.value); + + @override + List get props => [value]; +} diff --git a/lib/jmap/core/capability/capability_properties.dart b/lib/jmap/core/capability/capability_properties.dart new file mode 100644 index 0000000..7fd53e1 --- /dev/null +++ b/lib/jmap/core/capability/capability_properties.dart @@ -0,0 +1,5 @@ +import 'package:equatable/equatable.dart'; + +abstract class CapabilityProperties with EquatableMixin { + +} \ No newline at end of file diff --git a/lib/jmap/core/capability/core_capability.dart b/lib/jmap/core/capability/core_capability.dart new file mode 100644 index 0000000..3dfc2a8 --- /dev/null +++ b/lib/jmap/core/capability/core_capability.dart @@ -0,0 +1,48 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/http/converter/unsigned_int_converter.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'core_capability.g.dart'; + +@UnsignedIntConverter() +@JsonSerializable() +class CoreCapability extends CapabilityProperties with EquatableMixin { + final UnsignedInt maxSizeUpload; + final UnsignedInt maxConcurrentUpload; + final UnsignedInt maxSizeRequest; + final UnsignedInt maxConcurrentRequests; + final UnsignedInt maxCallsInRequest; + final UnsignedInt maxObjectsInGet; + final UnsignedInt maxObjectsInSet; + final Set collationAlgorithms; + + CoreCapability( + this.maxSizeUpload, + this.maxConcurrentUpload, + this.maxSizeRequest, + this.maxConcurrentRequests, + this.maxCallsInRequest, + this.maxObjectsInGet, + this.maxObjectsInSet, + this.collationAlgorithms); + + factory CoreCapability.fromJson(Map json) => _$CoreCapabilityFromJson(json); + + Map toJson() => _$CoreCapabilityToJson(this); + + static CoreCapability deserialize(Map json) => CoreCapability.fromJson(json); + + @override + List get props => [ + maxSizeUpload, + maxConcurrentUpload, + maxSizeRequest, + maxConcurrentRequests, + maxCallsInRequest, + maxObjectsInGet, + maxObjectsInSet, + collationAlgorithms + ]; +} \ No newline at end of file diff --git a/lib/jmap/core/capability/core_capability.g.dart b/lib/jmap/core/capability/core_capability.g.dart new file mode 100644 index 0000000..3727aec --- /dev/null +++ b/lib/jmap/core/capability/core_capability.g.dart @@ -0,0 +1,41 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'core_capability.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CoreCapability _$CoreCapabilityFromJson(Map json) { + return CoreCapability( + const UnsignedIntConverter().fromJson(json['maxSizeUpload'] as int), + const UnsignedIntConverter().fromJson(json['maxConcurrentUpload'] as int), + const UnsignedIntConverter().fromJson(json['maxSizeRequest'] as int), + const UnsignedIntConverter().fromJson(json['maxConcurrentRequests'] as int), + const UnsignedIntConverter().fromJson(json['maxCallsInRequest'] as int), + const UnsignedIntConverter().fromJson(json['maxObjectsInGet'] as int), + const UnsignedIntConverter().fromJson(json['maxObjectsInSet'] as int), + (json['collationAlgorithms'] as List) + .map((e) => e as String) + .toSet(), + ); +} + +Map _$CoreCapabilityToJson(CoreCapability instance) => + { + 'maxSizeUpload': + const UnsignedIntConverter().toJson(instance.maxSizeUpload), + 'maxConcurrentUpload': + const UnsignedIntConverter().toJson(instance.maxConcurrentUpload), + 'maxSizeRequest': + const UnsignedIntConverter().toJson(instance.maxSizeRequest), + 'maxConcurrentRequests': + const UnsignedIntConverter().toJson(instance.maxConcurrentRequests), + 'maxCallsInRequest': + const UnsignedIntConverter().toJson(instance.maxCallsInRequest), + 'maxObjectsInGet': + const UnsignedIntConverter().toJson(instance.maxObjectsInGet), + 'maxObjectsInSet': + const UnsignedIntConverter().toJson(instance.maxObjectsInSet), + 'collationAlgorithms': instance.collationAlgorithms.toList(), + }; diff --git a/lib/jmap/core/capability/default_capability.dart b/lib/jmap/core/capability/default_capability.dart new file mode 100644 index 0000000..c8eca3e --- /dev/null +++ b/lib/jmap/core/capability/default_capability.dart @@ -0,0 +1,11 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; + +class DefaultCapability extends CapabilityProperties with EquatableMixin { + final Map properties; + + DefaultCapability(this.properties); + + @override + List get props => [properties]; +} \ No newline at end of file diff --git a/lib/jmap/core/capability/mail_capability.dart b/lib/jmap/core/capability/mail_capability.dart new file mode 100644 index 0000000..398096b --- /dev/null +++ b/lib/jmap/core/capability/mail_capability.dart @@ -0,0 +1,48 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/http/converter/unsigned_int_converter.dart'; +import 'package:jmap_dart_client/http/converter/unsigned_int_nullable_converter.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'mail_capability.g.dart'; + +@UnsignedIntConverter() +@UnsignedIntNullableConverter() +@JsonSerializable() +class MailCapability extends CapabilityProperties with EquatableMixin { + + final UnsignedInt? maxMailboxesPerEmail; + final UnsignedInt? maxMailboxDepth; + final UnsignedInt maxSizeMailboxName; + final UnsignedInt maxSizeAttachmentsPerEmail; + final Set emailQuerySortOptions; + final bool mayCreateTopLevelMailbox; + + MailCapability( + this.maxMailboxesPerEmail, + this.maxMailboxDepth, + this.maxSizeMailboxName, + this.maxSizeAttachmentsPerEmail, + this.emailQuerySortOptions, + this.mayCreateTopLevelMailbox, + ); + + factory MailCapability.fromJson(Map json) => _$MailCapabilityFromJson(json); + + Map toJson() => _$MailCapabilityToJson(this); + + static MailCapability deserialize(Map json) { + return MailCapability.fromJson(json); + } + + @override + List get props => [ + maxMailboxesPerEmail, + maxMailboxDepth, + maxSizeMailboxName, + maxSizeAttachmentsPerEmail, + emailQuerySortOptions, + mayCreateTopLevelMailbox + ]; +} \ No newline at end of file diff --git a/lib/jmap/core/capability/mail_capability.g.dart b/lib/jmap/core/capability/mail_capability.g.dart new file mode 100644 index 0000000..a459bfe --- /dev/null +++ b/lib/jmap/core/capability/mail_capability.g.dart @@ -0,0 +1,37 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'mail_capability.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +MailCapability _$MailCapabilityFromJson(Map json) { + return MailCapability( + const UnsignedIntNullableConverter() + .fromJson(json['maxMailboxesPerEmail'] as int?), + const UnsignedIntNullableConverter() + .fromJson(json['maxMailboxDepth'] as int?), + const UnsignedIntConverter().fromJson(json['maxSizeMailboxName'] as int), + const UnsignedIntConverter() + .fromJson(json['maxSizeAttachmentsPerEmail'] as int), + (json['emailQuerySortOptions'] as List) + .map((e) => e as String) + .toSet(), + json['mayCreateTopLevelMailbox'] as bool, + ); +} + +Map _$MailCapabilityToJson(MailCapability instance) => + { + 'maxMailboxesPerEmail': const UnsignedIntNullableConverter() + .toJson(instance.maxMailboxesPerEmail), + 'maxMailboxDepth': + const UnsignedIntNullableConverter().toJson(instance.maxMailboxDepth), + 'maxSizeMailboxName': + const UnsignedIntConverter().toJson(instance.maxSizeMailboxName), + 'maxSizeAttachmentsPerEmail': const UnsignedIntConverter() + .toJson(instance.maxSizeAttachmentsPerEmail), + 'emailQuerySortOptions': instance.emailQuerySortOptions.toList(), + 'mayCreateTopLevelMailbox': instance.mayCreateTopLevelMailbox, + }; diff --git a/lib/jmap/core/capability/mdn_capability.dart b/lib/jmap/core/capability/mdn_capability.dart new file mode 100644 index 0000000..ca77bdc --- /dev/null +++ b/lib/jmap/core/capability/mdn_capability.dart @@ -0,0 +1,19 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'mdn_capability.g.dart'; + +@JsonSerializable() +class MdnCapability extends CapabilityProperties with EquatableMixin { + MdnCapability(); + + factory MdnCapability.fromJson(Map json) => _$MdnCapabilityFromJson(json); + + Map toJson() => _$MdnCapabilityToJson(this); + + static MdnCapability deserialize(Map json) => MdnCapability.fromJson(json); + + @override + List get props => []; +} \ No newline at end of file diff --git a/lib/jmap/core/capability/mdn_capability.g.dart b/lib/jmap/core/capability/mdn_capability.g.dart new file mode 100644 index 0000000..6881031 --- /dev/null +++ b/lib/jmap/core/capability/mdn_capability.g.dart @@ -0,0 +1,14 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'mdn_capability.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +MdnCapability _$MdnCapabilityFromJson(Map json) { + return MdnCapability(); +} + +Map _$MdnCapabilityToJson(MdnCapability instance) => + {}; diff --git a/lib/jmap/core/capability/submission_capability.dart b/lib/jmap/core/capability/submission_capability.dart new file mode 100644 index 0000000..ce6a231 --- /dev/null +++ b/lib/jmap/core/capability/submission_capability.dart @@ -0,0 +1,26 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/http/converter/unsigned_int_converter.dart'; +import 'package:jmap_dart_client/http/converter/unsigned_int_nullable_converter.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'submission_capability.g.dart'; + +@UnsignedIntConverter() +@JsonSerializable() +class SubmissionCapability extends CapabilityProperties with EquatableMixin { + final UnsignedInt maxDelayedSend; + final Set submissionExtensions; + + SubmissionCapability(this.maxDelayedSend, this.submissionExtensions); + + factory SubmissionCapability.fromJson(Map json) => _$SubmissionCapabilityFromJson(json); + + Map toJson() => _$SubmissionCapabilityToJson(this); + + static SubmissionCapability deserialize(Map json) => SubmissionCapability.fromJson(json); + + @override + List get props => [maxDelayedSend, submissionExtensions]; +} \ No newline at end of file diff --git a/lib/jmap/core/capability/submission_capability.g.dart b/lib/jmap/core/capability/submission_capability.g.dart new file mode 100644 index 0000000..92073ff --- /dev/null +++ b/lib/jmap/core/capability/submission_capability.g.dart @@ -0,0 +1,24 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'submission_capability.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SubmissionCapability _$SubmissionCapabilityFromJson(Map json) { + return SubmissionCapability( + const UnsignedIntConverter().fromJson(json['maxDelayedSend'] as int), + (json['submissionExtensions'] as List) + .map((e) => e as String) + .toSet(), + ); +} + +Map _$SubmissionCapabilityToJson( + SubmissionCapability instance) => + { + 'maxDelayedSend': + const UnsignedIntConverter().toJson(instance.maxDelayedSend), + 'submissionExtensions': instance.submissionExtensions.toList(), + }; diff --git a/lib/jmap/core/capability/vacation_capability.dart b/lib/jmap/core/capability/vacation_capability.dart new file mode 100644 index 0000000..1dfa92c --- /dev/null +++ b/lib/jmap/core/capability/vacation_capability.dart @@ -0,0 +1,18 @@ +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'vacation_capability.g.dart'; + +@JsonSerializable() +class VacationCapability extends CapabilityProperties { + VacationCapability(); + + factory VacationCapability.fromJson(Map json) => _$VacationCapabilityFromJson(json); + + Map toJson() => _$VacationCapabilityToJson(this); + + static VacationCapability deserialize(Map json) => VacationCapability.fromJson(json); + + @override + List get props => []; +} \ No newline at end of file diff --git a/lib/jmap/core/capability/vacation_capability.g.dart b/lib/jmap/core/capability/vacation_capability.g.dart new file mode 100644 index 0000000..cbc1a52 --- /dev/null +++ b/lib/jmap/core/capability/vacation_capability.g.dart @@ -0,0 +1,14 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'vacation_capability.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +VacationCapability _$VacationCapabilityFromJson(Map json) { + return VacationCapability(); +} + +Map _$VacationCapabilityToJson(VacationCapability instance) => + {}; diff --git a/lib/jmap/core/capability/websocket_capability.dart b/lib/jmap/core/capability/websocket_capability.dart new file mode 100644 index 0000000..b29f9f8 --- /dev/null +++ b/lib/jmap/core/capability/websocket_capability.dart @@ -0,0 +1,21 @@ +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'websocket_capability.g.dart'; + +@JsonSerializable() +class WebSocketCapability extends CapabilityProperties { + final bool supportsPush; + final Uri url; + + WebSocketCapability(this.supportsPush, this.url); + + factory WebSocketCapability.fromJson(Map json) => _$WebSocketCapabilityFromJson(json); + + Map toJson() => _$WebSocketCapabilityToJson(this); + + static WebSocketCapability deserialize(Map json) => WebSocketCapability.fromJson(json); + + @override + List get props => [supportsPush, url]; +} \ No newline at end of file diff --git a/lib/jmap/core/capability/websocket_capability.g.dart b/lib/jmap/core/capability/websocket_capability.g.dart new file mode 100644 index 0000000..dad9c95 --- /dev/null +++ b/lib/jmap/core/capability/websocket_capability.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'websocket_capability.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +WebSocketCapability _$WebSocketCapabilityFromJson(Map json) { + return WebSocketCapability( + json['supportsPush'] as bool, + Uri.parse(json['url'] as String), + ); +} + +Map _$WebSocketCapabilityToJson( + WebSocketCapability instance) => + { + 'supportsPush': instance.supportsPush, + 'url': instance.url.toString(), + }; diff --git a/lib/jmap/core/method/method.dart b/lib/jmap/core/method/method.dart index 9ae89c8..2c04f4e 100644 --- a/lib/jmap/core/method/method.dart +++ b/lib/jmap/core/method/method.dart @@ -1,6 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/capability/capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:jmap_dart_client/jmap/core/request/request_invocation.dart'; abstract class Method with EquatableMixin { diff --git a/lib/jmap/core/request/request_object.dart b/lib/jmap/core/request/request_object.dart index 161ee7e..1376193 100644 --- a/lib/jmap/core/request/request_object.dart +++ b/lib/jmap/core/request/request_object.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; -import 'package:jmap_dart_client/http/converter/capability_identifier_onverter.dart'; +import 'package:jmap_dart_client/http/converter/capability_identifier_converter.dart'; import 'package:jmap_dart_client/http/converter/request_invocation_converter.dart'; -import 'package:jmap_dart_client/jmap/core/capability/capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:jmap_dart_client/jmap/core/request/request_invocation.dart'; import 'package:jmap_dart_client/jmap/core/request/require_method_call.dart'; import 'package:jmap_dart_client/jmap/core/request/require_using.dart'; diff --git a/lib/jmap/core/request/require_using.dart b/lib/jmap/core/request/require_using.dart index 30487ec..e8d5aa4 100644 --- a/lib/jmap/core/request/require_using.dart +++ b/lib/jmap/core/request/require_using.dart @@ -1,7 +1,5 @@ - import 'package:built_collection/built_collection.dart'; - -import '../capability/capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; mixin RequiredUsing { final SetBuilder capabilitiesBuilder = SetBuilder(); diff --git a/lib/jmap/core/session/get_session.dart b/lib/jmap/core/session/get_session.dart new file mode 100644 index 0000000..31ecf80 --- /dev/null +++ b/lib/jmap/core/session/get_session.dart @@ -0,0 +1,38 @@ +import 'package:jmap_dart_client/http/converter/capabilities_converter.dart'; +import 'package:jmap_dart_client/http/http_client.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; + +class GetSession { + final HttpClient _httpClient; + final CapabilitiesConverter _capabilitiesConverter; + + GetSession(this._httpClient, this._capabilitiesConverter); + + Future execute() async { + return await _httpClient.get('/.well-known/jmap') + .then((value) => extractData(value)) + .catchError((error) => throw error); + } + + Session extractData(Map body) { + return Session.fromJson(body, converter: _capabilitiesConverter); + } +} + +class GetSessionBuilder { + final HttpClient _httpClient; + final CapabilitiesConverter _capabilitiesConverter = CapabilitiesConverter(); + + GetSessionBuilder(this._httpClient); + + void registerCapabilityConverter(Map)> converters) { + _capabilitiesConverter.addConverters(converters); + _capabilitiesConverter.build(); + } + + GetSession build() { + return GetSession(_httpClient, _capabilitiesConverter); + } +} \ No newline at end of file diff --git a/lib/jmap/core/session/session.dart b/lib/jmap/core/session/session.dart new file mode 100644 index 0000000..39bf7cc --- /dev/null +++ b/lib/jmap/core/session/session.dart @@ -0,0 +1,72 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/http/converter/account_converter.dart'; +import 'package:jmap_dart_client/http/converter/account_id_converter.dart'; +import 'package:jmap_dart_client/http/converter/capabilities_converter.dart'; +import 'package:jmap_dart_client/http/converter/state_converter.dart'; +import 'package:jmap_dart_client/http/converter/user_name_converter.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/account/account.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart'; +import 'package:jmap_dart_client/jmap/core/user_name.dart'; + +class Session with EquatableMixin { + + final Map capabilities; + final Map accounts; + final Map primaryAccounts; + final UserName username; + final Uri apiUrl; + final Uri downloadUrl; + final Uri uploadUrl; + final Uri eventSourceUrl; + final State state; + + Session( + this.capabilities, + this.accounts, + this.primaryAccounts, + this.username, + this.apiUrl, + this.downloadUrl, + this.uploadUrl, + this.eventSourceUrl, + this.state, + ); + + factory Session.fromJson(Map json, {CapabilitiesConverter? converter}) { + if (converter == null) { + converter = CapabilitiesConverter.defaultConverter; + } + return Session( + (json['capabilities'] as Map) + .map((key, value) => converter!.convert(key, value)), + (json['accounts'] as Map) + .map((key, value) => AccountConverter().convert(key, value, converter!)), + (json['primaryAccounts'] as Map) + .map((key, value) => MapEntry( + CapabilityIdentifier(Uri.parse(key)), + const AccountIdConverter().fromJson(value))), + const UserNameConverter().fromJson(json['username'] as String), + Uri.parse(json['apiUrl'] as String), + Uri.parse(json['downloadUrl'] as String), + Uri.parse(json['uploadUrl'] as String), + Uri.parse(json['eventSourceUrl'] as String), + const StateConverter().fromJson(json['state'] as String) + ); + } + + @override + List get props => [ + capabilities, + accounts, + primaryAccounts, + username, + apiUrl, + downloadUrl, + uploadUrl, + eventSourceUrl, + state, + ]; +} \ No newline at end of file diff --git a/lib/jmap/core/unsigned_int.dart b/lib/jmap/core/unsigned_int.dart index 1d8c382..d8c47ae 100644 --- a/lib/jmap/core/unsigned_int.dart +++ b/lib/jmap/core/unsigned_int.dart @@ -1,6 +1,7 @@ +import 'package:equatable/equatable.dart'; import 'package:quiver/check.dart'; -class UnsignedInt { +class UnsignedInt with EquatableMixin{ static final defaultValue = UnsignedInt(0); final num value; @@ -10,4 +11,7 @@ class UnsignedInt { checkArgument(value >= 0); checkArgument(value < 9007199254740992); } + + @override + List get props => [value]; } \ No newline at end of file diff --git a/lib/jmap/core/url_jmap.dart b/lib/jmap/core/url_jmap.dart new file mode 100644 index 0000000..a4b620c --- /dev/null +++ b/lib/jmap/core/url_jmap.dart @@ -0,0 +1,10 @@ +import 'package:equatable/equatable.dart'; + +class UrlJmap with EquatableMixin { + final String value; + + UrlJmap(this.value); + + @override + List get props => [value]; +} \ No newline at end of file diff --git a/lib/jmap/core/user_name.dart b/lib/jmap/core/user_name.dart new file mode 100644 index 0000000..bb0bf5e --- /dev/null +++ b/lib/jmap/core/user_name.dart @@ -0,0 +1,10 @@ +import 'package:equatable/equatable.dart'; + +class UserName with EquatableMixin { + final String value; + + UserName(this.value); + + @override + List get props => [value]; +} \ No newline at end of file diff --git a/lib/jmap/jmap_request.dart b/lib/jmap/jmap_request.dart index f397252..936e17c 100644 --- a/lib/jmap/jmap_request.dart +++ b/lib/jmap/jmap_request.dart @@ -1,4 +1,3 @@ - import 'package:built_collection/built_collection.dart'; import 'package:jmap_dart_client/http/http_client.dart'; import 'package:jmap_dart_client/jmap/core/request/reference_path.dart'; @@ -6,8 +5,7 @@ import 'package:jmap_dart_client/jmap/core/request/result_reference.dart'; import 'package:jmap_dart_client/jmap/core/response/response_object.dart'; import 'package:jmap_dart_client/util/util.dart'; import 'package:quiver/check.dart'; - -import 'core/capability/capability.dart'; +import 'core/capability/capability_identifier.dart'; import 'core/method/method.dart'; import 'core/request/request_invocation.dart'; import 'core/request/request_object.dart'; @@ -26,7 +24,7 @@ class JmapRequest { _requestObject = (RequestObject.builder() ..usings(_capabilities.asSet()) ..methodCalls(_invocations.values.toList())) - .build(); + .build(); return _httpClient.post('/jmap', data: _requestObject?.toJson()) .then((value) => extractData(value)) diff --git a/lib/jmap/mail/mailbox/changes/changes_mailbox_method.dart b/lib/jmap/mail/mailbox/changes/changes_mailbox_method.dart index b7bb372..fd3613a 100644 --- a/lib/jmap/mail/mailbox/changes/changes_mailbox_method.dart +++ b/lib/jmap/mail/mailbox/changes/changes_mailbox_method.dart @@ -2,7 +2,7 @@ import 'package:jmap_dart_client/http/converter/account_id_converter.dart'; import 'package:jmap_dart_client/http/converter/state_converter.dart'; import 'package:jmap_dart_client/http/converter/unsigned_int_nullable_converter.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/capability/capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:jmap_dart_client/jmap/core/method/request/changes_method.dart'; import 'package:jmap_dart_client/jmap/core/request/request_invocation.dart'; import 'package:jmap_dart_client/jmap/core/state.dart'; diff --git a/lib/jmap/mail/mailbox/get/get_mailbox_method.dart b/lib/jmap/mail/mailbox/get/get_mailbox_method.dart index b686140..fa8fd7f 100644 --- a/lib/jmap/mail/mailbox/get/get_mailbox_method.dart +++ b/lib/jmap/mail/mailbox/get/get_mailbox_method.dart @@ -2,7 +2,7 @@ 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/properties_converter.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/capability/capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.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:jmap_dart_client/jmap/core/request/result_reference.dart'; diff --git a/pubspec.lock b/pubspec.lock index c98d7d3..368e739 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,27 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "22.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.1" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" async: dependency: transitive description: @@ -15,6 +36,62 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + build_runner: + dependency: "direct main" + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "7.0.1" + built_collection: + dependency: "direct main" + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "8.1.1" characters: dependency: transitive description: @@ -29,6 +106,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.3" clock: dependency: transitive description: @@ -36,6 +127,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" collection: dependency: transitive description: @@ -43,6 +141,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" dartz: dependency: "direct main" description: @@ -50,6 +169,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.10.0-nullsafety.2" + dio: + dependency: "direct main" + description: + name: dio + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" equatable: dependency: "direct main" description: @@ -64,6 +190,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" flutter: dependency: "direct main" description: flutter @@ -74,6 +214,76 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.3" + json_annotation: + dependency: "direct main" + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" + json_serializable: + dependency: "direct main" + description: + name: json_serializable + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.3" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" matcher: dependency: transitive description: @@ -88,6 +298,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" path: dependency: transitive description: @@ -95,6 +319,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" quiver: dependency: "direct main" description: @@ -102,11 +354,32 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" source_span: dependency: transitive description: @@ -128,6 +401,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" string_scanner: dependency: transitive description: @@ -149,6 +429,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.3.0" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" typed_data: dependency: transitive description: @@ -163,6 +450,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" sdks: dart: ">=2.12.0 <3.0.0" flutter: ">=1.17.0" diff --git a/test/jmap/core/session/session_test.dart b/test/jmap/core/session/session_test.dart new file mode 100644 index 0000000..10ac414 --- /dev/null +++ b/test/jmap/core/session/session_test.dart @@ -0,0 +1,847 @@ +import 'dart:convert'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:jmap_dart_client/http/converter/capabilities_converter.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/account/account.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/capability/core_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/default_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/mail_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/mdn_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/submission_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/vacation_capability.dart'; +import 'package:jmap_dart_client/jmap/core/capability/websocket_capability.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/core/state.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:jmap_dart_client/jmap/core/user_name.dart'; + +import 'test_capability.dart'; + +void main() { + group('get session with default capabilities', () { + test('get should parsing correctly session', () { + final sessionString = '''{ + "capabilities": { + "urn:ietf:params:jmap:submission": { + "maxDelayedSend": 0, + "submissionExtensions": [] + }, + "urn:ietf:params:jmap:core": { + "maxSizeUpload": 20971520, + "maxConcurrentUpload": 4, + "maxSizeRequest": 10000000, + "maxConcurrentRequests": 4, + "maxCallsInRequest": 16, + "maxObjectsInGet": 500, + "maxObjectsInSet": 500, + "collationAlgorithms": [ + "i;unicode-casemap" + ] + }, + "urn:ietf:params:jmap:mail": { + "maxMailboxesPerEmail": 10000000, + "maxMailboxDepth": null, + "maxSizeMailboxName": 200, + "maxSizeAttachmentsPerEmail": 20000000, + "emailQuerySortOptions": [ + "receivedAt", + "sentAt", + "size", + "from", + "to", + "subject" + ], + "mayCreateTopLevelMailbox": true + }, + "urn:ietf:params:jmap:websocket": { + "supportsPush": true, + "url": "ws://domain.com/jmap/ws" + }, + "urn:apache:james:params:jmap:mail:quota": {}, + "urn:apache:james:params:jmap:mail:shares": {}, + "urn:ietf:params:jmap:vacationresponse": {}, + "urn:ietf:params:jmap:mdn": {} + }, + "accounts": { + "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6": { + "name": "bob@domain.tld", + "isPersonal": true, + "isReadOnly": false, + "accountCapabilities": { + "urn:ietf:params:jmap:submission": { + "maxDelayedSend": 0, + "submissionExtensions": [] + }, + "urn:ietf:params:jmap:websocket": { + "supportsPush": true, + "url": "ws://domain.com/jmap/ws" + }, + "urn:ietf:params:jmap:core": { + "maxSizeUpload": 20971520, + "maxConcurrentUpload": 4, + "maxSizeRequest": 10000000, + "maxConcurrentRequests": 4, + "maxCallsInRequest": 16, + "maxObjectsInGet": 500, + "maxObjectsInSet": 500, + "collationAlgorithms": [ + "i;unicode-casemap" + ] + }, + "urn:ietf:params:jmap:mail": { + "maxMailboxesPerEmail": 10000000, + "maxMailboxDepth": null, + "maxSizeMailboxName": 200, + "maxSizeAttachmentsPerEmail": 20000000, + "emailQuerySortOptions": [ + "receivedAt", + "sentAt", + "size", + "from", + "to", + "subject" + ], + "mayCreateTopLevelMailbox": true + }, + "urn:apache:james:params:jmap:mail:quota": {}, + "urn:apache:james:params:jmap:mail:shares": {}, + "urn:ietf:params:jmap:vacationresponse": {}, + "urn:ietf:params:jmap:mdn": {} + } + } + }, + "primaryAccounts": { + "urn:ietf:params:jmap:submission": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:websocket": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:core": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:mail": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:apache:james:params:jmap:mail:quota": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:apache:james:params:jmap:mail:shares": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:vacationresponse": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:mdn": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6" + }, + "username": "bob@domain.tld", + "apiUrl": "http://domain.com/jmap", + "downloadUrl": "http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}", + "uploadUrl": "http://domain.com/upload/{accountId}", + "eventSourceUrl": "http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}", + "state": "2c9f1b12-b35a-43e6-9af2-0106fb53a943" + }'''; + + final Session expectedSession = Session( + { + CapabilityIdentifier.jmapSubmission: SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapCore: CoreCapability( + UnsignedInt(20971520), + UnsignedInt(4), + UnsignedInt(10000000), + UnsignedInt(4), + UnsignedInt(16), + UnsignedInt(500), + UnsignedInt(500), + {"i;unicode-casemap"} + ), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true + ), + CapabilityIdentifier.jmapWebSocket: WebSocketCapability( + true, + Uri.parse('ws://domain.com/jmap/ws') + ), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:quota')): DefaultCapability(Map()), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:shares')): DefaultCapability(Map()), + CapabilityIdentifier.jmapVacationResponse: VacationCapability(), + CapabilityIdentifier.jmapMdn: MdnCapability() + }, + { + AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')): Account( + AccountName('bob@domain.tld'), + true, + false, + { + CapabilityIdentifier.jmapSubmission: SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapWebSocket: WebSocketCapability( + true, + Uri.parse('ws://domain.com/jmap/ws') + ), + CapabilityIdentifier.jmapCore: CoreCapability( + UnsignedInt(20971520), + UnsignedInt(4), + UnsignedInt(10000000), + UnsignedInt(4), + UnsignedInt(16), + UnsignedInt(500), + UnsignedInt(500), + {"i;unicode-casemap"} + ), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true + ), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:quota')): DefaultCapability(Map()), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:shares')): DefaultCapability(Map()), + CapabilityIdentifier.jmapVacationResponse: VacationCapability(), + CapabilityIdentifier.jmapMdn: MdnCapability() + } + ) + }, + { + CapabilityIdentifier.jmapSubmission: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapWebSocket: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapCore: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapMail: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:quota')): AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:shares')): AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapVacationResponse: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapMdn: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + }, + UserName('bob@domain.tld'), + Uri.parse('http://domain.com/jmap'), + Uri.parse('http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}'), + Uri.parse('http://domain.com/upload/{accountId}'), + Uri.parse('http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}'), + State('2c9f1b12-b35a-43e6-9af2-0106fb53a943') + ); + + final parsedSession = Session.fromJson(json.decode(sessionString)); + + expect(parsedSession, equals(expectedSession)); + }); + + test('get should parsing correctly session with some limit capabilities', () { + final sessionString = '''{ + "capabilities": { + "urn:ietf:params:jmap:submission": { + "maxDelayedSend": 0, + "submissionExtensions": [] + }, + "urn:ietf:params:jmap:core": { + "maxSizeUpload": 20971520, + "maxConcurrentUpload": 4, + "maxSizeRequest": 10000000, + "maxConcurrentRequests": 4, + "maxCallsInRequest": 16, + "maxObjectsInGet": 500, + "maxObjectsInSet": 500, + "collationAlgorithms": [ + "i;unicode-casemap" + ] + }, + "urn:ietf:params:jmap:mail": { + "maxMailboxesPerEmail": 10000000, + "maxMailboxDepth": null, + "maxSizeMailboxName": 200, + "maxSizeAttachmentsPerEmail": 20000000, + "emailQuerySortOptions": [ + "receivedAt", + "sentAt", + "size", + "from", + "to", + "subject" + ], + "mayCreateTopLevelMailbox": true + }, + "urn:ietf:params:jmap:vacationresponse": {} + }, + "accounts": { + "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6": { + "name": "bob@domain.tld", + "isPersonal": true, + "isReadOnly": false, + "accountCapabilities": { + "urn:ietf:params:jmap:submission": { + "maxDelayedSend": 0, + "submissionExtensions": [] + }, + "urn:ietf:params:jmap:core": { + "maxSizeUpload": 20971520, + "maxConcurrentUpload": 4, + "maxSizeRequest": 10000000, + "maxConcurrentRequests": 4, + "maxCallsInRequest": 16, + "maxObjectsInGet": 500, + "maxObjectsInSet": 500, + "collationAlgorithms": [ + "i;unicode-casemap" + ] + }, + "urn:ietf:params:jmap:mail": { + "maxMailboxesPerEmail": 10000000, + "maxMailboxDepth": null, + "maxSizeMailboxName": 200, + "maxSizeAttachmentsPerEmail": 20000000, + "emailQuerySortOptions": [ + "receivedAt", + "sentAt", + "size", + "from", + "to", + "subject" + ], + "mayCreateTopLevelMailbox": true + }, + "urn:ietf:params:jmap:vacationresponse": {} + } + } + }, + "primaryAccounts": { + "urn:ietf:params:jmap:submission": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:core": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:mail": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:vacationresponse": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6" + }, + "username": "bob@domain.tld", + "apiUrl": "http://domain.com/jmap", + "downloadUrl": "http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}", + "uploadUrl": "http://domain.com/upload/{accountId}", + "eventSourceUrl": "http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}", + "state": "2c9f1b12-b35a-43e6-9af2-0106fb53a943" + }'''; + + final Session expectedSession = Session( + { + CapabilityIdentifier.jmapSubmission: SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapCore: CoreCapability( + UnsignedInt(20971520), + UnsignedInt(4), + UnsignedInt(10000000), + UnsignedInt(4), + UnsignedInt(16), + UnsignedInt(500), + UnsignedInt(500), + {"i;unicode-casemap"} + ), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true + ), + CapabilityIdentifier.jmapVacationResponse: VacationCapability() + }, + { + AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')): Account( + AccountName('bob@domain.tld'), + true, + false, + { + CapabilityIdentifier.jmapSubmission: SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapCore: CoreCapability( + UnsignedInt(20971520), + UnsignedInt(4), + UnsignedInt(10000000), + UnsignedInt(4), + UnsignedInt(16), + UnsignedInt(500), + UnsignedInt(500), + {"i;unicode-casemap"} + ), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true + ), + CapabilityIdentifier.jmapVacationResponse: VacationCapability() + } + ) + }, + { + CapabilityIdentifier.jmapSubmission: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapCore: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapMail: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapVacationResponse: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')) + }, + UserName('bob@domain.tld'), + Uri.parse('http://domain.com/jmap'), + Uri.parse('http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}'), + Uri.parse('http://domain.com/upload/{accountId}'), + Uri.parse('http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}'), + State('2c9f1b12-b35a-43e6-9af2-0106fb53a943') + ); + + final parsedSession = Session.fromJson(json.decode(sessionString)); + + expect(parsedSession, equals(expectedSession)); + }); + }); + + group('get session with unknown capability', () { + test('get should parsing correctly session with default converter', () { + final sessionString = '''{ + "capabilities": { + "urn:tmail:custom:params:mailbox": { + "param1": 1, + "param2": "good", + "param3": ["custom", "capability"] + }, + "urn:ietf:params:jmap:submission": { + "maxDelayedSend": 0, + "submissionExtensions": [] + }, + "urn:ietf:params:jmap:core": { + "maxSizeUpload": 20971520, + "maxConcurrentUpload": 4, + "maxSizeRequest": 10000000, + "maxConcurrentRequests": 4, + "maxCallsInRequest": 16, + "maxObjectsInGet": 500, + "maxObjectsInSet": 500, + "collationAlgorithms": [ + "i;unicode-casemap" + ] + }, + "urn:ietf:params:jmap:mail": { + "maxMailboxesPerEmail": 10000000, + "maxMailboxDepth": null, + "maxSizeMailboxName": 200, + "maxSizeAttachmentsPerEmail": 20000000, + "emailQuerySortOptions": [ + "receivedAt", + "sentAt", + "size", + "from", + "to", + "subject" + ], + "mayCreateTopLevelMailbox": true + }, + "urn:ietf:params:jmap:websocket": { + "supportsPush": true, + "url": "ws://domain.com/jmap/ws" + }, + "urn:apache:james:params:jmap:mail:quota": {}, + "urn:apache:james:params:jmap:mail:shares": {}, + "urn:ietf:params:jmap:vacationresponse": {}, + "urn:ietf:params:jmap:mdn": {} + }, + "accounts": { + "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6": { + "name": "bob@domain.tld", + "isPersonal": true, + "isReadOnly": false, + "accountCapabilities": { + "urn:tmail:custom:params:mailbox": { + "param1": 1, + "param2": "good", + "param3": ["custom", "capability"] + }, + "urn:ietf:params:jmap:submission": { + "maxDelayedSend": 0, + "submissionExtensions": [] + }, + "urn:ietf:params:jmap:websocket": { + "supportsPush": true, + "url": "ws://domain.com/jmap/ws" + }, + "urn:ietf:params:jmap:core": { + "maxSizeUpload": 20971520, + "maxConcurrentUpload": 4, + "maxSizeRequest": 10000000, + "maxConcurrentRequests": 4, + "maxCallsInRequest": 16, + "maxObjectsInGet": 500, + "maxObjectsInSet": 500, + "collationAlgorithms": [ + "i;unicode-casemap" + ] + }, + "urn:ietf:params:jmap:mail": { + "maxMailboxesPerEmail": 10000000, + "maxMailboxDepth": null, + "maxSizeMailboxName": 200, + "maxSizeAttachmentsPerEmail": 20000000, + "emailQuerySortOptions": [ + "receivedAt", + "sentAt", + "size", + "from", + "to", + "subject" + ], + "mayCreateTopLevelMailbox": true + }, + "urn:apache:james:params:jmap:mail:quota": {}, + "urn:apache:james:params:jmap:mail:shares": {}, + "urn:ietf:params:jmap:vacationresponse": {}, + "urn:ietf:params:jmap:mdn": {} + } + } + }, + "primaryAccounts": { + "urn:tmail:custom:params:mailbox": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:submission": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:websocket": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:core": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:mail": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:apache:james:params:jmap:mail:quota": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:apache:james:params:jmap:mail:shares": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:vacationresponse": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:mdn": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6" + }, + "username": "bob@domain.tld", + "apiUrl": "http://domain.com/jmap", + "downloadUrl": "http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}", + "uploadUrl": "http://domain.com/upload/{accountId}", + "eventSourceUrl": "http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}", + "state": "2c9f1b12-b35a-43e6-9af2-0106fb53a943" + }'''; + final customCapabilityIdentifier = CapabilityIdentifier(Uri.parse('urn:tmail:custom:params:mailbox')); + final customCapability = DefaultCapability( + { + "param1": 1, + "param2": "good", + "param3": {"custom", "capability"}.toList() + } + ); + + final Session expectedSession = Session( + { + customCapabilityIdentifier: customCapability, + CapabilityIdentifier.jmapSubmission: SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapCore: CoreCapability( + UnsignedInt(20971520), + UnsignedInt(4), + UnsignedInt(10000000), + UnsignedInt(4), + UnsignedInt(16), + UnsignedInt(500), + UnsignedInt(500), + {"i;unicode-casemap"} + ), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true + ), + CapabilityIdentifier.jmapWebSocket: WebSocketCapability( + true, + Uri.parse('ws://domain.com/jmap/ws') + ), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:quota')): DefaultCapability(Map()), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:shares')): DefaultCapability(Map()), + CapabilityIdentifier.jmapVacationResponse: VacationCapability(), + CapabilityIdentifier.jmapMdn: MdnCapability() + }, + { + AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')): Account( + AccountName('bob@domain.tld'), + true, + false, + { + customCapabilityIdentifier: customCapability, + CapabilityIdentifier.jmapSubmission: SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapWebSocket: WebSocketCapability( + true, + Uri.parse('ws://domain.com/jmap/ws') + ), + CapabilityIdentifier.jmapCore: CoreCapability( + UnsignedInt(20971520), + UnsignedInt(4), + UnsignedInt(10000000), + UnsignedInt(4), + UnsignedInt(16), + UnsignedInt(500), + UnsignedInt(500), + {"i;unicode-casemap"} + ), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true + ), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:quota')): DefaultCapability(Map()), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:shares')): DefaultCapability(Map()), + CapabilityIdentifier.jmapVacationResponse: VacationCapability(), + CapabilityIdentifier.jmapMdn: MdnCapability() + } + ) + }, + { + customCapabilityIdentifier: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapSubmission: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapWebSocket: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapCore: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapMail: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:quota')): AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:shares')): AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapVacationResponse: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapMdn: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + }, + UserName('bob@domain.tld'), + Uri.parse('http://domain.com/jmap'), + Uri.parse('http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}'), + Uri.parse('http://domain.com/upload/{accountId}'), + Uri.parse('http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}'), + State('2c9f1b12-b35a-43e6-9af2-0106fb53a943') + ); + + final parsedSession = Session.fromJson(json.decode(sessionString)); + + expect(parsedSession, equals(expectedSession)); + expect(parsedSession.capabilities[CapabilityIdentifier(Uri.parse('urn:tmail:custom:params:mailbox'))], equals(customCapability)); + expect(parsedSession.accounts.values.first + .accountCapabilities[CapabilityIdentifier(Uri.parse('urn:tmail:custom:params:mailbox'))], + equals(customCapability)); + }); + }); + + group('get session with custom capability', () { + test('get should parsing correctly with relevant custom converter', () { + final sessionString = '''{ + "capabilities": { + "urn:test:tmail:params:custom": { + "testParam1": 100, + "testParam2": "test", + "testParam3": ["test", "capability"] + }, + "urn:tmail:custom:params:mailbox": { + "param1": 1, + "param2": "good", + "param3": ["custom", "capability"] + }, + "urn:ietf:params:jmap:submission": { + "maxDelayedSend": 0, + "submissionExtensions": [] + }, + "urn:ietf:params:jmap:core": { + "maxSizeUpload": 20971520, + "maxConcurrentUpload": 4, + "maxSizeRequest": 10000000, + "maxConcurrentRequests": 4, + "maxCallsInRequest": 16, + "maxObjectsInGet": 500, + "maxObjectsInSet": 500, + "collationAlgorithms": [ + "i;unicode-casemap" + ] + }, + "urn:ietf:params:jmap:mail": { + "maxMailboxesPerEmail": 10000000, + "maxMailboxDepth": null, + "maxSizeMailboxName": 200, + "maxSizeAttachmentsPerEmail": 20000000, + "emailQuerySortOptions": [ + "receivedAt", + "sentAt", + "size", + "from", + "to", + "subject" + ], + "mayCreateTopLevelMailbox": true + }, + "urn:ietf:params:jmap:websocket": { + "supportsPush": true, + "url": "ws://domain.com/jmap/ws" + }, + "urn:apache:james:params:jmap:mail:quota": {}, + "urn:apache:james:params:jmap:mail:shares": {}, + "urn:ietf:params:jmap:vacationresponse": {}, + "urn:ietf:params:jmap:mdn": {} + }, + "accounts": { + "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6": { + "name": "bob@domain.tld", + "isPersonal": true, + "isReadOnly": false, + "accountCapabilities": { + "urn:tmail:custom:params:mailbox": { + "param1": 1, + "param2": "good", + "param3": ["custom", "capability"] + }, + "urn:ietf:params:jmap:submission": { + "maxDelayedSend": 0, + "submissionExtensions": [] + }, + "urn:ietf:params:jmap:websocket": { + "supportsPush": true, + "url": "ws://domain.com/jmap/ws" + }, + "urn:ietf:params:jmap:core": { + "maxSizeUpload": 20971520, + "maxConcurrentUpload": 4, + "maxSizeRequest": 10000000, + "maxConcurrentRequests": 4, + "maxCallsInRequest": 16, + "maxObjectsInGet": 500, + "maxObjectsInSet": 500, + "collationAlgorithms": [ + "i;unicode-casemap" + ] + }, + "urn:ietf:params:jmap:mail": { + "maxMailboxesPerEmail": 10000000, + "maxMailboxDepth": null, + "maxSizeMailboxName": 200, + "maxSizeAttachmentsPerEmail": 20000000, + "emailQuerySortOptions": [ + "receivedAt", + "sentAt", + "size", + "from", + "to", + "subject" + ], + "mayCreateTopLevelMailbox": true + }, + "urn:apache:james:params:jmap:mail:quota": {}, + "urn:apache:james:params:jmap:mail:shares": {}, + "urn:ietf:params:jmap:vacationresponse": {}, + "urn:ietf:params:jmap:mdn": {} + } + } + }, + "primaryAccounts": { + "urn:tmail:custom:params:mailbox": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:submission": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:websocket": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:core": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:mail": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:apache:james:params:jmap:mail:quota": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:apache:james:params:jmap:mail:shares": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:vacationresponse": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + "urn:ietf:params:jmap:mdn": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6" + }, + "username": "bob@domain.tld", + "apiUrl": "http://domain.com/jmap", + "downloadUrl": "http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}", + "uploadUrl": "http://domain.com/upload/{accountId}", + "eventSourceUrl": "http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}", + "state": "2c9f1b12-b35a-43e6-9af2-0106fb53a943" + }'''; + final customCapabilityIdentifier = CapabilityIdentifier(Uri.parse('urn:tmail:custom:params:mailbox')); + final customCapability = DefaultCapability( + { + "param1": 1, + "param2": "good", + "param3": {"custom", "capability"}.toList() + } + ); + + final testCapability = TestCapability(100, 'test', {"test", "capability"}); + + final Session expectedSession = Session( + { + TestCapability.testCapabilityIdentifier: testCapability, + customCapabilityIdentifier: customCapability, + CapabilityIdentifier.jmapSubmission: SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapCore: CoreCapability( + UnsignedInt(20971520), + UnsignedInt(4), + UnsignedInt(10000000), + UnsignedInt(4), + UnsignedInt(16), + UnsignedInt(500), + UnsignedInt(500), + {"i;unicode-casemap"} + ), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true + ), + CapabilityIdentifier.jmapWebSocket: WebSocketCapability( + true, + Uri.parse('ws://domain.com/jmap/ws') + ), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:quota')): DefaultCapability(Map()), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:shares')): DefaultCapability(Map()), + CapabilityIdentifier.jmapVacationResponse: VacationCapability(), + CapabilityIdentifier.jmapMdn: MdnCapability() + }, + { + AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')): Account( + AccountName('bob@domain.tld'), + true, + false, + { + customCapabilityIdentifier: customCapability, + CapabilityIdentifier.jmapSubmission: SubmissionCapability(UnsignedInt(0), Set()), + CapabilityIdentifier.jmapWebSocket: WebSocketCapability( + true, + Uri.parse('ws://domain.com/jmap/ws') + ), + CapabilityIdentifier.jmapCore: CoreCapability( + UnsignedInt(20971520), + UnsignedInt(4), + UnsignedInt(10000000), + UnsignedInt(4), + UnsignedInt(16), + UnsignedInt(500), + UnsignedInt(500), + {"i;unicode-casemap"} + ), + CapabilityIdentifier.jmapMail: MailCapability( + UnsignedInt(10000000), + null, + UnsignedInt(200), + UnsignedInt(20000000), + {"receivedAt", "sentAt", "size", "from", "to", "subject"}, + true + ), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:quota')): DefaultCapability(Map()), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:shares')): DefaultCapability(Map()), + CapabilityIdentifier.jmapVacationResponse: VacationCapability(), + CapabilityIdentifier.jmapMdn: MdnCapability() + } + ) + }, + { + customCapabilityIdentifier: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapSubmission: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapWebSocket: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapCore: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapMail: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:quota')): AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier(Uri.parse('urn:apache:james:params:jmap:mail:shares')): AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapVacationResponse: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + CapabilityIdentifier.jmapMdn: AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')), + }, + UserName('bob@domain.tld'), + Uri.parse('http://domain.com/jmap'), + Uri.parse('http://domain.com/download/{accountId}/{blobId}/?type={type}&name={name}'), + Uri.parse('http://domain.com/upload/{accountId}'), + Uri.parse('http://domain.com/eventSource?types={types}&closeAfter={closeafter}&ping={ping}'), + State('2c9f1b12-b35a-43e6-9af2-0106fb53a943') + ); + + final parsedSession = Session.fromJson( + json.decode(sessionString), + converter: CapabilitiesConverter() + ..addConverters({TestCapability.testCapabilityIdentifier: TestCapability.deserialize}) + ..build() + ); + + expect(parsedSession, equals(expectedSession)); + expect(parsedSession.capabilities[TestCapability.testCapabilityIdentifier], equals(testCapability)); + }); + }); +} diff --git a/test/jmap/core/session/test_capability.dart b/test/jmap/core/session/test_capability.dart new file mode 100644 index 0000000..b2c9d5c --- /dev/null +++ b/test/jmap/core/session/test_capability.dart @@ -0,0 +1,26 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_properties.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'test_capability.g.dart'; + +@JsonSerializable() +class TestCapability extends CapabilityProperties { + static CapabilityIdentifier testCapabilityIdentifier = CapabilityIdentifier(Uri.parse('urn:test:tmail:params:custom')); + + final int testParam1; + final String testParam2; + final Set testParam3; + + TestCapability(this.testParam1, this.testParam2, this.testParam3); + + factory TestCapability.fromJson(Map json) => _$TestCapabilityFromJson(json); + + Map toJson() => _$TestCapabilityToJson(this); + + static TestCapability deserialize(Map json) => TestCapability.fromJson(json); + + @override + List get props => [testParam1, testParam2, testParam3]; +} \ No newline at end of file diff --git a/test/jmap/core/session/test_capability.g.dart b/test/jmap/core/session/test_capability.g.dart new file mode 100644 index 0000000..3b2ae83 --- /dev/null +++ b/test/jmap/core/session/test_capability.g.dart @@ -0,0 +1,22 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'test_capability.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +TestCapability _$TestCapabilityFromJson(Map json) { + return TestCapability( + json['testParam1'] as int, + json['testParam2'] as String, + (json['testParam3'] as List).map((e) => e as String).toSet(), + ); +} + +Map _$TestCapabilityToJson(TestCapability instance) => + { + 'testParam1': instance.testParam1, + 'testParam2': instance.testParam2, + 'testParam3': instance.testParam3.toList(), + };