TF-4169 Add Label/set method
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
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:labels/labels.dart';
|
||||||
|
|
||||||
|
class SetLabelMethod extends SetMethod<Label> {
|
||||||
|
SetLabelMethod(super.accountId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
MethodName get methodName => MethodName('Label/set');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<CapabilityIdentifier> get requiredCapabilities => {
|
||||||
|
CapabilityIdentifier.jmapCore,
|
||||||
|
LabelsConstants.labelsCapability,
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final val = <String, dynamic>{
|
||||||
|
'accountId': const AccountIdConverter().toJson(accountId),
|
||||||
|
};
|
||||||
|
|
||||||
|
void writeNotNull(String key, dynamic value) {
|
||||||
|
if (value != null) {
|
||||||
|
val[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeNotNull('ifInState', ifInState?.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(),
|
||||||
|
);
|
||||||
|
writeNotNull('#destroy', referenceDestroy?.toJson());
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [accountId, ifInState, create, update, destroy];
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
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:labels/model/label.dart';
|
||||||
|
|
||||||
|
class SetLabelResponse extends SetResponse<Label> {
|
||||||
|
SetLabelResponse(
|
||||||
|
super.accountId, {
|
||||||
|
super.newState,
|
||||||
|
super.oldState,
|
||||||
|
super.created,
|
||||||
|
super.updated,
|
||||||
|
super.destroyed,
|
||||||
|
super.notCreated,
|
||||||
|
super.notUpdated,
|
||||||
|
super.notDestroyed,
|
||||||
|
});
|
||||||
|
|
||||||
|
static SetLabelResponse deserialize(Map<String, dynamic> json) {
|
||||||
|
return SetLabelResponse(
|
||||||
|
const AccountIdConverter().fromJson(json['accountId'] as String),
|
||||||
|
newState:
|
||||||
|
const StateNullableConverter().fromJson(json['newState'] as String?),
|
||||||
|
oldState:
|
||||||
|
const StateNullableConverter().fromJson(json['oldState'] as String?),
|
||||||
|
created: (json['created'] as Map<String, dynamic>?)?.map((key, value) =>
|
||||||
|
MapEntry(const IdConverter().fromJson(key),
|
||||||
|
Label.fromJson(value as Map<String, dynamic>))),
|
||||||
|
updated: (json['updated'] as Map<String, dynamic>?)?.map((key, value) =>
|
||||||
|
MapEntry(
|
||||||
|
const IdConverter().fromJson(key),
|
||||||
|
value != null
|
||||||
|
? Label.fromJson(value as Map<String, dynamic>)
|
||||||
|
: null)),
|
||||||
|
destroyed: (json['destroyed'] as List<dynamic>?)
|
||||||
|
?.map((id) => const IdConverter().fromJson(id))
|
||||||
|
.toSet(),
|
||||||
|
notCreated: (json['notCreated'] as Map<String, dynamic>?)?.map(
|
||||||
|
(key, value) => MapEntry(
|
||||||
|
const IdConverter().fromJson(key), SetError.fromJson(value))),
|
||||||
|
notUpdated: (json['notUpdated'] as Map<String, dynamic>?)?.map(
|
||||||
|
(key, value) => MapEntry(
|
||||||
|
const IdConverter().fromJson(key), SetError.fromJson(value))),
|
||||||
|
notDestroyed: (json['notDestroyed'] as Map<String, dynamic>?)?.map(
|
||||||
|
(key, value) => MapEntry(
|
||||||
|
const IdConverter().fromJson(key), SetError.fromJson(value))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
oldState,
|
||||||
|
newState,
|
||||||
|
created,
|
||||||
|
updated,
|
||||||
|
destroyed,
|
||||||
|
notCreated,
|
||||||
|
notUpdated,
|
||||||
|
notDestroyed
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -16,12 +16,17 @@ part 'label.g.dart';
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
class Label with EquatableMixin {
|
class Label with EquatableMixin {
|
||||||
final Id id;
|
final Id? id;
|
||||||
final String displayName;
|
final String? keyword;
|
||||||
final String keyword;
|
final String? displayName;
|
||||||
final HexColor? color;
|
final HexColor? color;
|
||||||
|
|
||||||
Label(this.id, this.displayName, this.keyword, this.color);
|
Label({
|
||||||
|
this.id,
|
||||||
|
this.keyword,
|
||||||
|
this.displayName,
|
||||||
|
this.color,
|
||||||
|
});
|
||||||
|
|
||||||
factory Label.fromJson(Map<String, dynamic> json) => _$LabelFromJson(json);
|
factory Label.fromJson(Map<String, dynamic> json) => _$LabelFromJson(json);
|
||||||
|
|
||||||
@@ -30,8 +35,8 @@ class Label with EquatableMixin {
|
|||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
id,
|
id,
|
||||||
displayName,
|
|
||||||
keyword,
|
keyword,
|
||||||
|
displayName,
|
||||||
color,
|
color,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,20 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:http_mock_adapter/http_mock_adapter.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/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/jmap_request.dart';
|
|
||||||
import 'package:labels/labels.dart';
|
import 'package:labels/labels.dart';
|
||||||
import 'package:labels/method/get/get_label_method.dart';
|
import 'package:labels/method/get/get_label_method.dart';
|
||||||
import 'package:labels/method/get/get_label_response.dart';
|
import 'package:labels/method/get/get_label_response.dart';
|
||||||
|
|
||||||
|
import '../method_fixtures.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('GetLabelMethod – additional test cases', () {
|
group('GetLabelMethod', () {
|
||||||
final accountId = AccountId(
|
final accountId = AccountId(
|
||||||
Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6'),
|
Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6'),
|
||||||
);
|
);
|
||||||
|
|
||||||
Dio createDio() {
|
|
||||||
final dio = Dio(BaseOptions(method: 'POST'))
|
|
||||||
..options.baseUrl = 'http://domain.com/jmap';
|
|
||||||
return dio;
|
|
||||||
}
|
|
||||||
|
|
||||||
JmapRequestBuilder createBuilder(Dio dio) {
|
|
||||||
return JmapRequestBuilder(
|
|
||||||
HttpClient(dio),
|
|
||||||
ProcessingInvocation(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> buildRequestPayload({
|
Map<String, dynamic> buildRequestPayload({
|
||||||
required List<String> ids,
|
required List<String> ids,
|
||||||
}) {
|
}) {
|
||||||
@@ -54,8 +41,18 @@ void main() {
|
|||||||
final dio = createDio();
|
final dio = createDio();
|
||||||
final adapter = DioAdapter(dio: dio);
|
final adapter = DioAdapter(dio: dio);
|
||||||
|
|
||||||
final labelA = Label(Id('A'), 'Label A', 'labelA', HexColor('#111111'));
|
final labelA = Label(
|
||||||
final labelB = Label(Id('B'), 'Label B', 'labelB', HexColor('#222222'));
|
id: Id('A'),
|
||||||
|
keyword: 'labelA',
|
||||||
|
displayName: 'Label A',
|
||||||
|
color: HexColor('#111111'),
|
||||||
|
);
|
||||||
|
final labelB = Label(
|
||||||
|
id: Id('B'),
|
||||||
|
keyword: 'labelB',
|
||||||
|
displayName: 'Label B',
|
||||||
|
color: HexColor('#222222'),
|
||||||
|
);
|
||||||
|
|
||||||
adapter.onPost(
|
adapter.onPost(
|
||||||
'',
|
'',
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:jmap_dart_client/http/http_client.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/jmap_request.dart';
|
||||||
|
|
||||||
|
Dio createDio() {
|
||||||
|
final dio = Dio(BaseOptions(method: 'POST'))
|
||||||
|
..options.baseUrl = 'http://domain.com/jmap';
|
||||||
|
return dio;
|
||||||
|
}
|
||||||
|
|
||||||
|
JmapRequestBuilder createBuilder(Dio dio) {
|
||||||
|
return JmapRequestBuilder(
|
||||||
|
HttpClient(dio),
|
||||||
|
ProcessingInvocation(),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
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/jmap/account_id.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
|
import 'package:labels/labels.dart';
|
||||||
|
import 'package:labels/method/set/set_label_method.dart';
|
||||||
|
import 'package:labels/method/set/set_label_response.dart';
|
||||||
|
|
||||||
|
import '../method_fixtures.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('SetLabelMethod', () {
|
||||||
|
final accountId = AccountId(
|
||||||
|
Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6'),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> buildPayload(Map<String, dynamic> createMap) {
|
||||||
|
return {
|
||||||
|
"using": [
|
||||||
|
"urn:ietf:params:jmap:core",
|
||||||
|
"com:linagora:params:jmap:labels"
|
||||||
|
],
|
||||||
|
"methodCalls": [
|
||||||
|
[
|
||||||
|
"Label/set",
|
||||||
|
{
|
||||||
|
"accountId": accountId.id.value,
|
||||||
|
"create": createMap,
|
||||||
|
},
|
||||||
|
"c0"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test('should deserialize SetLabelResponse correctly', () async {
|
||||||
|
// Arrange
|
||||||
|
final dio = createDio();
|
||||||
|
final adapter = DioAdapter(dio: dio);
|
||||||
|
|
||||||
|
adapter.onPost(
|
||||||
|
'',
|
||||||
|
(server) => server.reply(200, {
|
||||||
|
"sessionState": "session-state",
|
||||||
|
"methodResponses": [
|
||||||
|
[
|
||||||
|
"Label/set",
|
||||||
|
{
|
||||||
|
"accountId": accountId.id.value,
|
||||||
|
"oldState": "old",
|
||||||
|
"newState": "new",
|
||||||
|
"created": {
|
||||||
|
"4f29": {"id": "123456", "keyword": "important"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"c0"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
data: buildPayload({
|
||||||
|
"4f29": {"displayName": "Important", "color": "#00ccdd"}
|
||||||
|
}),
|
||||||
|
headers: {"accept": "application/json;jmapVersion=rfc-8621"},
|
||||||
|
);
|
||||||
|
|
||||||
|
final builder = createBuilder(dio);
|
||||||
|
final method = SetLabelMethod(accountId)
|
||||||
|
..addCreate(
|
||||||
|
Id('4f29'),
|
||||||
|
Label(
|
||||||
|
displayName: 'Important',
|
||||||
|
color: HexColor('#00ccdd'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final invocation = builder.invocation(method);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final response = await (builder..usings(method.requiredCapabilities))
|
||||||
|
.build()
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
final parsed = response.parse<SetLabelResponse>(
|
||||||
|
invocation.methodCallId,
|
||||||
|
SetLabelResponse.deserialize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(parsed, isNotNull);
|
||||||
|
expect(parsed!.created![Id('4f29')]!.keyword, equals('important'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should process multiple created labels', () async {
|
||||||
|
// Arrange
|
||||||
|
final dio = createDio();
|
||||||
|
final adapter = DioAdapter(dio: dio);
|
||||||
|
|
||||||
|
adapter.onPost(
|
||||||
|
'',
|
||||||
|
(server) => server.reply(200, {
|
||||||
|
"sessionState": "S",
|
||||||
|
"methodResponses": [
|
||||||
|
[
|
||||||
|
"Label/set",
|
||||||
|
{
|
||||||
|
"accountId": accountId.id.value,
|
||||||
|
"newState": "S2",
|
||||||
|
"created": {
|
||||||
|
"A": {"id": "111", "keyword": "tagA"},
|
||||||
|
"B": {"id": "222", "keyword": "tagB"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"c0"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
data: buildPayload({
|
||||||
|
"A": {"displayName": "Tag A", "color": "#111111"},
|
||||||
|
"B": {"displayName": "Tag B", "color": "#222222"},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
final builder = createBuilder(dio);
|
||||||
|
|
||||||
|
final method = SetLabelMethod(accountId)
|
||||||
|
..addCreate(
|
||||||
|
Id('A'), Label(displayName: "Tag A", color: HexColor("#111111")))
|
||||||
|
..addCreate(
|
||||||
|
Id('B'), Label(displayName: "Tag B", color: HexColor("#222222")));
|
||||||
|
|
||||||
|
final invocation = builder.invocation(method);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final response = await (builder..usings(method.requiredCapabilities))
|
||||||
|
.build()
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
final parsed = response.parse<SetLabelResponse>(
|
||||||
|
invocation.methodCallId,
|
||||||
|
SetLabelResponse.deserialize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(parsed, isNotNull);
|
||||||
|
expect(parsed!.created!.length, equals(2));
|
||||||
|
expect(parsed.created![Id('A')]!.keyword, equals('tagA'));
|
||||||
|
expect(parsed.created![Id('B')]!.keyword, equals('tagB'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should throw DioException when backend returns 500', () async {
|
||||||
|
// Arrange
|
||||||
|
final dio = createDio();
|
||||||
|
final adapter = DioAdapter(dio: dio);
|
||||||
|
|
||||||
|
adapter.onPost(
|
||||||
|
'',
|
||||||
|
(server) => server.reply(500, {}),
|
||||||
|
data: buildPayload({
|
||||||
|
"A": {"displayName": "Err", "color": "#dddddd"}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
final builder = createBuilder(dio);
|
||||||
|
|
||||||
|
final method = SetLabelMethod(accountId)
|
||||||
|
..addCreate(
|
||||||
|
Id("A"),
|
||||||
|
Label(displayName: "Err", color: HexColor("#dddddd")),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final call =
|
||||||
|
(builder..usings(method.requiredCapabilities)).build().execute();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(call, throwsA(isA<DioError>()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user