Skip to content

Commit f284944

Browse files
authored
fix: TypeError on addRelation function (#1098)
1 parent 76eeca4 commit f284944

File tree

2 files changed

+134
-2
lines changed

2 files changed

+134
-2
lines changed

packages/dart/lib/src/objects/parse_relation.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class _ParseRelation<T extends ParseObject>
6161

6262
// For offline caching, we keep track of every object
6363
// we've known to be in the relation.
64-
Set<T> knownObjects = <T>{};
64+
Set<ParseObject> knownObjects = <ParseObject>{};
6565

6666
_ParseRelationOperation? lastPreformedOperation;
6767

@@ -90,7 +90,7 @@ class _ParseRelation<T extends ParseObject>
9090

9191
lastPreformedOperation = relationOperation;
9292

93-
knownObjects = lastPreformedOperation!.value.toSet() as Set<T>;
93+
knownObjects = lastPreformedOperation!.value.toSet();
9494

9595
return this;
9696
}

packages/dart/test/src/objects/parse_object/parse_object_relation_test.dart

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,5 +624,137 @@ void main() {
624624
// assert
625625
expect(() => getRelation(), throwsA(isA<ParseRelationException>()));
626626
});
627+
628+
test('addRelation() should work with custom ParseObject subclasses', () {
629+
// arrange
630+
// Create custom ParseObject subclasses similar to the issue report
631+
final contact1 = Contact()..objectId = 'contact1';
632+
final contact2 = Contact()..objectId = 'contact2';
633+
634+
final order = Order();
635+
636+
// act & assert
637+
// This should not throw a TypeError
638+
expect(
639+
() => order.addRelation('receivers', [contact1, contact2]),
640+
returnsNormally,
641+
);
642+
643+
final toJsonAfterAddRelation = order.toJson(forApiRQ: true);
644+
645+
const expectedToJson = {
646+
"receivers": {
647+
"__op": "AddRelation",
648+
"objects": [
649+
{
650+
"__type": "Pointer",
651+
"className": "Contact",
652+
"objectId": "contact1",
653+
},
654+
{
655+
"__type": "Pointer",
656+
"className": "Contact",
657+
"objectId": "contact2",
658+
},
659+
],
660+
},
661+
};
662+
663+
expect(
664+
DeepCollectionEquality().equals(expectedToJson, toJsonAfterAddRelation),
665+
isTrue,
666+
);
667+
});
668+
669+
test(
670+
'addRelation() should work when getRelation<T>() was called first with typed generic',
671+
() {
672+
// This test reproduces issue #999
673+
// The issue occurs when:
674+
// 1. getRelation<Contact>() is called first (creating _ParseRelation<Contact>)
675+
// 2. Then addRelation() is called with Contact objects
676+
// 3. The merge operation creates a Set<ParseObject>
677+
// 4. Trying to cast Set<ParseObject> to Set<Contact> throws TypeError
678+
679+
// arrange
680+
final contact1 = Contact()..objectId = 'contact1';
681+
final contact2 = Contact()..objectId = 'contact2';
682+
683+
final order = Order();
684+
685+
// First, get the relation with typed generic (this creates _ParseRelation<Contact>)
686+
order.getRelation<Contact>('receivers');
687+
688+
// act & assert
689+
// This should NOT throw: _TypeError (type '_Set<ParseObject>' is not a subtype of type 'Set<Contact>' in type cast)
690+
expect(
691+
() => order.addRelation('receivers', [contact1, contact2]),
692+
returnsNormally,
693+
);
694+
},
695+
);
696+
697+
test(
698+
'calling addRelation() multiple times with custom subclasses should work',
699+
() {
700+
// arrange
701+
final contact1 = Contact()..objectId = 'contact1';
702+
final contact2 = Contact()..objectId = 'contact2';
703+
final contact3 = Contact()..objectId = 'contact3';
704+
705+
final order = Order();
706+
707+
// act & assert
708+
// First addRelation call
709+
expect(
710+
() => order.addRelation('receivers', [contact1]),
711+
returnsNormally,
712+
);
713+
714+
// Second addRelation call - this should also not throw
715+
expect(
716+
() => order.addRelation('receivers', [contact2, contact3]),
717+
returnsNormally,
718+
);
719+
},
720+
);
721+
722+
test('removeRelation() should work with custom ParseObject subclasses', () {
723+
// arrange
724+
final contact1 = Contact()..objectId = 'contact1';
725+
final contact2 = Contact()..objectId = 'contact2';
726+
727+
final order = Order();
728+
729+
// act & assert
730+
expect(
731+
() => order.removeRelation('receivers', [contact1, contact2]),
732+
returnsNormally,
733+
);
734+
});
627735
});
628736
}
737+
738+
/// Custom ParseObject subclass for testing (similar to the issue report)
739+
class Contact extends ParseObject implements ParseCloneable {
740+
Contact() : super(_keyTableName);
741+
Contact.clone() : this();
742+
743+
@override
744+
clone(Map<String, dynamic> map) =>
745+
Contact.clone()..fromJson(Map<String, dynamic>.from(map));
746+
747+
static const String _keyTableName = 'Contact';
748+
}
749+
750+
/// Custom ParseObject subclass for testing (similar to the issue report)
751+
class Order extends ParseObject implements ParseCloneable {
752+
Order() : super(_keyTableName);
753+
Order.clone() : this();
754+
755+
@override
756+
clone(Map<String, dynamic> map) =>
757+
Order.clone()..fromJson(Map<String, dynamic>.from(map));
758+
759+
static const String _keyTableName = 'Order';
760+
}

0 commit comments

Comments
 (0)