From 2997d562f50e4abb6ced0b6852827715ffba19f7 Mon Sep 17 00:00:00 2001 From: oneRain Date: Wed, 1 Dec 2021 11:43:38 +0800 Subject: [PATCH] fix: LCObject serialization --- lib/internal/codec/lc_decoder.dart | 4 +++ lib/internal/codec/lc_encoder.dart | 15 ++++++++-- lib/internal/object/lc_object_data.dart | 9 ++++++ lib/internal/operation/lc_add_operation.dart | 4 ++- .../operation/lc_add_relation_operation.dart | 4 ++- .../operation/lc_add_unique_operation.dart | 7 ++--- .../operation/lc_decrement_operation.dart | 4 ++- .../operation/lc_delete_operation.dart | 4 ++- .../operation/lc_increment_operation.dart | 4 ++- lib/internal/operation/lc_operation.dart | 28 +++++++++++++++++++ .../operation/lc_remove_operation.dart | 4 ++- .../lc_remove_relation_operation.dart | 7 ++--- lib/lc_object.dart | 22 +++++++++++---- test/object_test.dart | 14 +++++++--- 14 files changed, 104 insertions(+), 26 deletions(-) diff --git a/lib/internal/codec/lc_decoder.dart b/lib/internal/codec/lc_decoder.dart index f84bf54..36738aa 100644 --- a/lib/internal/codec/lc_decoder.dart +++ b/lib/internal/codec/lc_decoder.dart @@ -80,4 +80,8 @@ class _LCDecoder { }); return acl; } + + static _LCOperation decodeOperation(dynamic data) { + return _LCOperation.decode(data); + } } diff --git a/lib/internal/codec/lc_encoder.dart b/lib/internal/codec/lc_encoder.dart index 36f44cc..71dbf1a 100644 --- a/lib/internal/codec/lc_encoder.dart +++ b/lib/internal/codec/lc_encoder.dart @@ -74,9 +74,18 @@ class _LCEncoder { if (object.updatedAt != null) { data['updatedAt'] = object.updatedAt.toString(); } - object._estimatedData.forEach((k, v) { - data[k] = _LCEncoder.encode(v, full: full); - }); + if (object._data.customPropertyMap.length > 0) { + object._data.customPropertyMap.forEach((k, v) { + data[k] = _LCEncoder.encode(v, full: full); + }); + } + if (object._operationMap.length > 0) { + Map opMap = new Map(); + object._operationMap.forEach((k, v) { + opMap[k] = encodeOperation(v); + }); + data['__operationMap'] = opMap; + } } return data; } diff --git a/lib/internal/object/lc_object_data.dart b/lib/internal/object/lc_object_data.dart index 69909ad..8a65da4 100644 --- a/lib/internal/object/lc_object_data.dart +++ b/lib/internal/object/lc_object_data.dart @@ -12,6 +12,8 @@ class _LCObjectData { late Map customPropertyMap; + Map? operationMap; + _LCObjectData() { customPropertyMap = new Map(); } @@ -30,6 +32,13 @@ class _LCObjectData { } else { if (key == 'ACL' && value is Map) { result.customPropertyMap[key] = _LCDecoder.decodeACL(value); + } else if (key == '__operationMap' && value is Map) { + // 操作记录 + Map opMap = new Map(); + value.forEach((k, v) { + opMap[k] = _LCDecoder.decodeOperation(v); + }); + result.operationMap = opMap; } else { // 自定义属性 result.customPropertyMap[key] = _LCDecoder.decode(value); diff --git a/lib/internal/operation/lc_add_operation.dart b/lib/internal/operation/lc_add_operation.dart index 8ef9ea4..0e19f00 100644 --- a/lib/internal/operation/lc_add_operation.dart +++ b/lib/internal/operation/lc_add_operation.dart @@ -1,6 +1,8 @@ part of leancloud_storage; class _LCAddOperation extends _LCOperation { + static const String OP = 'Add'; + late List valueList; _LCAddOperation(Iterable values) { @@ -16,7 +18,7 @@ class _LCAddOperation extends _LCOperation { @override encode() { - return {'__op': 'Add', 'objects': _LCEncoder.encodeList(valueList)}; + return {'__op': OP, 'objects': _LCEncoder.encodeList(valueList)}; } @override diff --git a/lib/internal/operation/lc_add_relation_operation.dart b/lib/internal/operation/lc_add_relation_operation.dart index 4064aea..6919d0e 100644 --- a/lib/internal/operation/lc_add_relation_operation.dart +++ b/lib/internal/operation/lc_add_relation_operation.dart @@ -1,6 +1,8 @@ part of leancloud_storage; class _LCAddRelationOperation extends _LCOperation { + static const String OP = 'AddRelation'; + late List valueList; _LCAddRelationOperation(dynamic value) { @@ -16,7 +18,7 @@ class _LCAddRelationOperation extends _LCOperation { @override encode() { - return {'__op': 'AddRelation', 'objects': _LCEncoder.encodeList(valueList)}; + return {'__op': OP, 'objects': _LCEncoder.encodeList(valueList)}; } @override diff --git a/lib/internal/operation/lc_add_unique_operation.dart b/lib/internal/operation/lc_add_unique_operation.dart index f912cd6..e996533 100644 --- a/lib/internal/operation/lc_add_unique_operation.dart +++ b/lib/internal/operation/lc_add_unique_operation.dart @@ -1,6 +1,8 @@ part of leancloud_storage; class _LCAddUniqueOperation extends _LCOperation { + static const String OP = 'AddUnique'; + late Set values; _LCAddUniqueOperation(Iterable values) { @@ -16,10 +18,7 @@ class _LCAddUniqueOperation extends _LCOperation { @override encode() { - return { - '__op': 'AddUnique', - 'objects': _LCEncoder.encodeList(values.toList()) - }; + return {'__op': OP, 'objects': _LCEncoder.encodeList(values.toList())}; } @override diff --git a/lib/internal/operation/lc_decrement_operation.dart b/lib/internal/operation/lc_decrement_operation.dart index 1b805da..cce747f 100644 --- a/lib/internal/operation/lc_decrement_operation.dart +++ b/lib/internal/operation/lc_decrement_operation.dart @@ -1,6 +1,8 @@ part of leancloud_storage; class _LCDecrementOperation extends _LCOperation { + static const String OP = 'Decrement'; + num value; _LCDecrementOperation(this.value); @@ -15,7 +17,7 @@ class _LCDecrementOperation extends _LCOperation { @override encode() { - return {'__op': 'Decrement', 'amount': value}; + return {'__op': OP, 'amount': value}; } @override diff --git a/lib/internal/operation/lc_delete_operation.dart b/lib/internal/operation/lc_delete_operation.dart index a237268..66fedb7 100644 --- a/lib/internal/operation/lc_delete_operation.dart +++ b/lib/internal/operation/lc_delete_operation.dart @@ -2,6 +2,8 @@ part of leancloud_storage; /// Deletion class _LCDeleteOperation extends _LCOperation { + static const String OP = 'Delete'; + @override _LCOperation mergeWithPrevious(_LCOperation previousOp) { return this; @@ -9,7 +11,7 @@ class _LCDeleteOperation extends _LCOperation { @override encode() { - return {'__op': 'Delete'}; + return {'__op': OP}; } @override diff --git a/lib/internal/operation/lc_increment_operation.dart b/lib/internal/operation/lc_increment_operation.dart index bfc6de1..104e3df 100644 --- a/lib/internal/operation/lc_increment_operation.dart +++ b/lib/internal/operation/lc_increment_operation.dart @@ -1,6 +1,8 @@ part of leancloud_storage; class _LCIncrementOperation extends _LCOperation { + static const String OP = 'Increment'; + num value; _LCIncrementOperation(this.value); @@ -15,7 +17,7 @@ class _LCIncrementOperation extends _LCOperation { @override encode() { - return {'__op': 'Increment', 'amount': value}; + return {'__op': OP, 'amount': value}; } @override diff --git a/lib/internal/operation/lc_operation.dart b/lib/internal/operation/lc_operation.dart index ec4b339..ee659c6 100644 --- a/lib/internal/operation/lc_operation.dart +++ b/lib/internal/operation/lc_operation.dart @@ -12,4 +12,32 @@ abstract class _LCOperation { // 得到增加的对象 List? getNewObjectList(); + + static _LCOperation decode(dynamic data) { + if (!(data is Map) || !data.containsKey('__op')) { + // set + return new _LCSetOperation(_LCDecoder.decode(data)); + } + String op = data['__op']; + switch (op) { + case _LCAddOperation.OP: + return new _LCAddOperation(_LCDecoder.decode(data['objects'])); + case _LCAddRelationOperation.OP: + return new _LCAddRelationOperation(_LCDecoder.decode(data['objects'])); + case _LCAddUniqueOperation.OP: + return new _LCAddUniqueOperation(_LCDecoder.decode(data['objects'])); + case _LCDecrementOperation.OP: + return new _LCDecrementOperation(data['amount']); + case _LCDeleteOperation.OP: + return new _LCDeleteOperation(); + case _LCIncrementOperation.OP: + return new _LCIncrementOperation(data['amount']); + case _LCRemoveOperation.OP: + return new _LCRemoveOperation(_LCDecoder.decode(data['objects'])); + case _LCRemoveRelationOperation.OP: + return new _LCRemoveRelationOperation(data['objects']); + default: + throw ('Error operation: ${jsonEncode(data)}'); + } + } } diff --git a/lib/internal/operation/lc_remove_operation.dart b/lib/internal/operation/lc_remove_operation.dart index 9c4b9b4..565a1af 100644 --- a/lib/internal/operation/lc_remove_operation.dart +++ b/lib/internal/operation/lc_remove_operation.dart @@ -1,6 +1,8 @@ part of leancloud_storage; class _LCRemoveOperation extends _LCOperation { + static const String OP = 'Remove'; + late List valueList; _LCRemoveOperation(Iterable values) { @@ -18,7 +20,7 @@ class _LCRemoveOperation extends _LCOperation { @override encode() { - return {'__op': 'Remove', 'objects': _LCEncoder.encodeList(valueList)}; + return {'__op': OP, 'objects': _LCEncoder.encodeList(valueList)}; } @override diff --git a/lib/internal/operation/lc_remove_relation_operation.dart b/lib/internal/operation/lc_remove_relation_operation.dart index 37b0e4e..9f56142 100644 --- a/lib/internal/operation/lc_remove_relation_operation.dart +++ b/lib/internal/operation/lc_remove_relation_operation.dart @@ -1,6 +1,8 @@ part of leancloud_storage; class _LCRemoveRelationOperation extends _LCOperation { + static const String OP = 'RemoveRelation'; + late List valueList; _LCRemoveRelationOperation(dynamic value) { @@ -16,10 +18,7 @@ class _LCRemoveRelationOperation extends _LCOperation { @override encode() { - return { - '__op': 'RemoveRelation', - 'objects': _LCEncoder.encodeList(valueList.toList()) - }; + return {'__op': OP, 'objects': _LCEncoder.encodeList(valueList.toList())}; } @override diff --git a/lib/lc_object.dart b/lib/lc_object.dart index ef12563..526151a 100644 --- a/lib/lc_object.dart +++ b/lib/lc_object.dart @@ -247,21 +247,33 @@ class LCObject { if (data == null) { return; } + _data.className = data.className ?? _data.className; _data.objectId = data.objectId ?? _data.objectId; _data.createdAt = data.createdAt ?? _data.createdAt; _data.updatedAt = data.updatedAt ?? _data.updatedAt; + // 先将本地的预估数据直接替换 _data.customPropertyMap = _estimatedData; + // 再将服务端的数据覆盖 data.customPropertyMap.forEach((String key, dynamic value) { _data.customPropertyMap[key] = value; }); - // 最后重新生成预估数据,用于后续访问和操作 - _rebuildEstimatedData(); - // 清空操作 - _operationMap.clear(); - _isDirty = false; + + // 最后根据 data 情况确定如何合并 + if (data.operationMap != null && data.operationMap!.length > 0) { + // 如果有保存操作 + data.operationMap!.forEach((k, v) { + _applyOperation(k, v); + }); + } else { + // 清空操作 + _operationMap.clear(); + // 最后重新生成预估数据,用于后续访问和操作 + _rebuildEstimatedData(); + _isDirty = false; + } } /// Fetches the object from the cloud. diff --git a/test/object_test.dart b/test/object_test.dart index fbe08fe..e7d855e 100644 --- a/test/object_test.dart +++ b/test/object_test.dart @@ -1,9 +1,12 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:leancloud_storage/leancloud.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'utils.dart'; void main() { + SharedPreferences.setMockInitialValues({}); + group('object', () { setUp(() => initNorthChina()); @@ -162,11 +165,11 @@ void main() { test('serialization', () async { LCObject object = new LCObject('Hello'); - object['intValue'] = 123; + object.increment('intValue', 12); object['boolValue'] = true; object['stringValue'] = 'hello, world'; object['time'] = DateTime.now(); - object['intList'] = [1, 1, 2, 3, 5, 8]; + object.addAll('intList', [1, 1, 2, 3, 5, 8]); object['stringMap'] = {'k1': 111, 'k2': true, 'k3': 'haha'}; LCObject nestedObj = new LCObject('World'); nestedObj['content'] = '7788'; @@ -175,16 +178,19 @@ void main() { World world = new World(); world.content = 'hello, world'; object['pointerList'] = [world, new LCObject('World')]; - await object.save(); + // String json = object.toString(); LCLogger.debug(json); LCObject newObj = LCObject.parseObject(json); + + newObj = await object.save(); + assert(newObj.objectId != null); assert(newObj.className != null); assert(newObj.createdAt != null); assert(newObj.updatedAt != null); - assert(newObj['intValue'] == 123); + assert(newObj['intValue'] == 12); assert(newObj['boolValue'] == true); assert(newObj['stringValue'] == 'hello, world'); assert(newObj['objectValue']['content'] == '7788');