Skip to content

Commit a8cf4f3

Browse files
authored
fix: Invalid JSON in whereMatchesQuery due to extra quotes around limiters (#1100)
1 parent 027855d commit a8cf4f3

File tree

2 files changed

+122
-2
lines changed

2 files changed

+122
-2
lines changed

packages/dart/lib/src/network/parse_query.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ class QueryBuilder<T extends ParseObject> {
616616
String _buildQueryRelational(String className) {
617617
queries = _checkForMultipleColumnInstances(queries);
618618
String lim = getLimitersRelational(limiters);
619-
return '{"where":{${buildQueries(queries)}},"className":"$className"${limiters.isNotEmpty ? ',"$lim"' : ''}}';
619+
return '{"where":{${buildQueries(queries)}},"className":"$className"${lim.isNotEmpty ? ',$lim' : ''}}';
620620
}
621621

622622
/// Builds the query relational with Key for Parse

packages/dart/test/src/network/parse_query_test.dart

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,127 @@ void main() {
534534
);
535535

536536
// assert
537-
expect(result.query.contains("%22object2%22,%22%22include%22"), true);
537+
expect(result.query.contains("%22object2%22,%22include%22"), true);
538+
});
539+
540+
test('whereMatchesQuery generates valid JSON', () async {
541+
// arrange - This test specifically checks for the bug where
542+
// whereMatchesQuery generated invalid JSON with trailing commas
543+
// See: https://github.com/parse-community/Parse-SDK-Flutter/issues/932
544+
ParseObject deliveryArea = ParseObject("DeliveryArea");
545+
final deliveryAreasQuery = QueryBuilder<ParseObject>(deliveryArea)
546+
..whereArrayContainsAll('postalCodes', [21075]);
547+
548+
ParseObject farmer = ParseObject("Farmer", client: client);
549+
final query = QueryBuilder<ParseObject>(farmer)
550+
..whereMatchesQuery('deliveryAreas', deliveryAreasQuery)
551+
..whereEqualTo('isActive', true);
552+
553+
var desiredOutput = {"results": []};
554+
555+
when(
556+
client.get(
557+
any,
558+
options: anyNamed("options"),
559+
onReceiveProgress: anyNamed("onReceiveProgress"),
560+
),
561+
).thenAnswer(
562+
(_) async => ParseNetworkResponse(
563+
statusCode: 200,
564+
data: jsonEncode(desiredOutput),
565+
),
566+
);
567+
568+
// act
569+
await query.query();
570+
571+
final String capturedUrl = verify(
572+
client.get(
573+
captureAny,
574+
options: anyNamed("options"),
575+
onReceiveProgress: anyNamed("onReceiveProgress"),
576+
),
577+
).captured.single;
578+
579+
// Extract the 'where' parameter from the URL
580+
final Uri uri = Uri.parse(capturedUrl);
581+
final String? whereParam = uri.queryParameters['where'];
582+
583+
// assert - The JSON should be valid (no trailing commas)
584+
expect(whereParam, isNotNull);
585+
586+
// This should not throw if JSON is valid
587+
final decoded = jsonDecode(whereParam!);
588+
expect(decoded, isA<Map>());
589+
590+
// Verify the structure is correct
591+
expect(decoded['deliveryAreas'], isNotNull);
592+
expect(decoded['deliveryAreas']['\$inQuery'], isNotNull);
593+
expect(
594+
decoded['deliveryAreas']['\$inQuery']['className'],
595+
'DeliveryArea',
596+
);
597+
expect(decoded['deliveryAreas']['\$inQuery']['where'], isNotNull);
598+
});
599+
600+
test('whereMatchesQuery with limiters should not have extra quotes', () async {
601+
// arrange - This test specifically checks for the bug where limiters
602+
// were incorrectly wrapped with extra quotes like ',"$lim"' instead of ',$lim'
603+
// which produced patterns like ""include" (double quotes before limiter key)
604+
// See: https://github.com/parse-community/Parse-SDK-Flutter/issues/932
605+
ParseObject innerObject = ParseObject("InnerClass");
606+
final innerQuery = QueryBuilder<ParseObject>(innerObject)
607+
..includeObject(["relatedField"])
608+
..whereEqualTo("status", "active");
609+
610+
ParseObject outerObject = ParseObject("OuterClass", client: client);
611+
final outerQuery = QueryBuilder<ParseObject>(outerObject)
612+
..whereMatchesQuery('innerRef', innerQuery);
613+
614+
var desiredOutput = {"results": []};
615+
616+
when(
617+
client.get(
618+
any,
619+
options: anyNamed("options"),
620+
onReceiveProgress: anyNamed("onReceiveProgress"),
621+
),
622+
).thenAnswer(
623+
(_) async => ParseNetworkResponse(
624+
statusCode: 200,
625+
data: jsonEncode(desiredOutput),
626+
),
627+
);
628+
629+
// act
630+
await outerQuery.query();
631+
632+
final String capturedUrl = verify(
633+
client.get(
634+
captureAny,
635+
options: anyNamed("options"),
636+
onReceiveProgress: anyNamed("onReceiveProgress"),
637+
),
638+
).captured.single;
639+
640+
// assert - Check that the URL does NOT contain the buggy pattern ""include"
641+
// (double quotes before 'include' which was caused by extra quotes around limiters)
642+
// %22%22 is the URL-encoded form of "" (two consecutive double quotes)
643+
expect(
644+
capturedUrl.contains('%22%22include'),
645+
isFalse,
646+
reason: 'URL should not contain double quotes before limiter keys',
647+
);
648+
649+
// Also verify the correct pattern exists: className followed by comma and include
650+
// without extra quotes between them
651+
// The pattern should be: "className":"InnerClass","include" not "className":"InnerClass",""include"
652+
expect(
653+
capturedUrl.contains('%22InnerClass%22,%22include%22'),
654+
isTrue,
655+
reason:
656+
'URL should contain properly formatted className followed by include',
657+
);
538658
});
539659

540660
test('the result query should contains encoded special characters values', () {

0 commit comments

Comments
 (0)