Skip to content

Commit 2aab833

Browse files
committed
api: Add updateMessageFlags and updateMessageFlagsForNarrow routes
1 parent f162fd0 commit 2aab833

File tree

4 files changed

+246
-1
lines changed

4 files changed

+246
-1
lines changed

lib/api/model/model.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ sealed class Message {
419419
Map<String, dynamic> toJson();
420420
}
421421

422-
/// As in [Message.flags].
422+
/// https://zulip.com/api/update-message-flags#available-flags
423423
@JsonEnum(fieldRename: FieldRename.snake, alwaysCreate: true)
424424
enum MessageFlag {
425425
read,

lib/api/route/messages.dart

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,88 @@ Future<void> removeReaction(ApiConnection connection, {
290290
'reaction_type': RawParameter(reactionType.toJson()),
291291
});
292292
}
293+
294+
/// https://zulip.com/api/update-message-flags
295+
Future<UpdateMessageFlagsResult> updateMessageFlags(ApiConnection connection, {
296+
required List<int> messages,
297+
required UpdateMessageFlagsOp op,
298+
required MessageFlag flag,
299+
}) {
300+
return connection.post('updateMessageFlags', UpdateMessageFlagsResult.fromJson, 'messages/flags', {
301+
'messages': messages,
302+
'op': RawParameter(op.toJson()),
303+
'flag': RawParameter(flag.toJson()),
304+
});
305+
}
306+
307+
/// An `op` value for [updateMessageFlags] and [updateMessageFlagsForNarrow].
308+
@JsonEnum(fieldRename: FieldRename.snake, alwaysCreate: true)
309+
enum UpdateMessageFlagsOp {
310+
add,
311+
remove;
312+
313+
String toJson() => _$UpdateMessageFlagsOpEnumMap[this]!;
314+
}
315+
316+
@JsonSerializable(fieldRename: FieldRename.snake)
317+
class UpdateMessageFlagsResult {
318+
final List<int> messages;
319+
320+
UpdateMessageFlagsResult({
321+
required this.messages,
322+
});
323+
324+
factory UpdateMessageFlagsResult.fromJson(Map<String, dynamic> json) =>
325+
_$UpdateMessageFlagsResultFromJson(json);
326+
327+
Map<String, dynamic> toJson() => _$UpdateMessageFlagsResultToJson(this);
328+
}
329+
330+
/// https://zulip.com/api/update-message-flags-for-narrow
331+
///
332+
/// This binding only supports feature levels 155+.
333+
// TODO(server-6) remove FL 155+ mention in doc, and the related `assert`
334+
Future<UpdateMessageFlagsForNarrowResult> updateMessageFlagsForNarrow(ApiConnection connection, {
335+
required Anchor anchor,
336+
bool? includeAnchor,
337+
required int numBefore,
338+
required int numAfter,
339+
required ApiNarrow narrow,
340+
required UpdateMessageFlagsOp op,
341+
required MessageFlag flag,
342+
}) {
343+
assert(connection.zulipFeatureLevel! >= 155);
344+
return connection.post('updateMessageFlagsForNarrow', UpdateMessageFlagsForNarrowResult.fromJson, 'messages/flags/narrow', {
345+
'anchor': RawParameter(anchor.toJson()),
346+
if (includeAnchor != null) 'include_anchor': includeAnchor,
347+
'num_before': numBefore,
348+
'num_after': numAfter,
349+
'narrow': resolveDmElements(narrow, connection.zulipFeatureLevel!),
350+
'op': RawParameter(op.toJson()),
351+
'flag': RawParameter(flag.toJson()),
352+
});
353+
}
354+
355+
@JsonSerializable(fieldRename: FieldRename.snake)
356+
class UpdateMessageFlagsForNarrowResult {
357+
final int processedCount;
358+
final int updatedCount;
359+
final int? firstProcessedId;
360+
final int? lastProcessedId;
361+
final bool foundOldest;
362+
final bool foundNewest;
363+
364+
UpdateMessageFlagsForNarrowResult({
365+
required this.processedCount,
366+
required this.updatedCount,
367+
required this.firstProcessedId,
368+
required this.lastProcessedId,
369+
required this.foundOldest,
370+
required this.foundNewest,
371+
});
372+
373+
factory UpdateMessageFlagsForNarrowResult.fromJson(Map<String, dynamic> json) =>
374+
_$UpdateMessageFlagsForNarrowResultFromJson(json);
375+
376+
Map<String, dynamic> toJson() => _$UpdateMessageFlagsForNarrowResultToJson(this);
377+
}

lib/api/route/messages.g.dart

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/api/route/messages_test.dart

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,4 +451,124 @@ void main() {
451451
});
452452
});
453453
});
454+
455+
group('updateMessageFlags', () {
456+
Future<UpdateMessageFlagsResult> checkUpdateMessageFlags(
457+
FakeApiConnection connection, {
458+
required List<int> messages,
459+
required UpdateMessageFlagsOp op,
460+
required MessageFlag flag,
461+
required Map<String, String> expected,
462+
}) async {
463+
final result = await updateMessageFlags(connection,
464+
messages: messages, op: op, flag: flag);
465+
check(connection.lastRequest).isNotNull().isA<http.Request>()
466+
..method.equals('POST')
467+
..url.path.equals('/api/v1/messages/flags')
468+
..bodyFields.deepEquals(expected);
469+
return result;
470+
}
471+
472+
test('smoke', () {
473+
return FakeApiConnection.with_((connection) async {
474+
connection.prepare(json:
475+
UpdateMessageFlagsResult(messages: [1, 2]).toJson());
476+
await checkUpdateMessageFlags(connection,
477+
messages: [1, 2, 3],
478+
op: UpdateMessageFlagsOp.add, flag: MessageFlag.read,
479+
expected: {
480+
'messages': jsonEncode([1, 2, 3]),
481+
'op': 'add',
482+
'flag': 'read',
483+
});
484+
});
485+
});
486+
});
487+
488+
group('updateMessageFlagsForNarrow', () {
489+
Future<UpdateMessageFlagsForNarrowResult> checkUpdateMessageFlagsForNarrow(
490+
FakeApiConnection connection, {
491+
required Anchor anchor,
492+
required int numBefore,
493+
required int numAfter,
494+
required ApiNarrow narrow,
495+
required UpdateMessageFlagsOp op,
496+
required MessageFlag flag,
497+
required Map<String, String> expected,
498+
}) async {
499+
final result = await updateMessageFlagsForNarrow(connection,
500+
anchor: anchor, numBefore: numBefore, numAfter: numAfter,
501+
narrow: narrow, op: op, flag: flag);
502+
check(connection.lastRequest).isNotNull().isA<http.Request>()
503+
..method.equals('POST')
504+
..url.path.equals('/api/v1/messages/flags/narrow')
505+
..bodyFields.deepEquals(expected);
506+
return result;
507+
}
508+
509+
mkResult({required bool foundOldest}) =>
510+
UpdateMessageFlagsForNarrowResult(
511+
processedCount: 11, updatedCount: 3,
512+
firstProcessedId: null, lastProcessedId: null,
513+
foundOldest: foundOldest, foundNewest: true);
514+
515+
test('smoke', () {
516+
return FakeApiConnection.with_((connection) async {
517+
connection.prepare(json: mkResult(foundOldest: true).toJson());
518+
await checkUpdateMessageFlagsForNarrow(connection,
519+
anchor: AnchorCode.oldest,
520+
numBefore: 0, numAfter: 20,
521+
narrow: const AllMessagesNarrow().apiEncode(),
522+
op: UpdateMessageFlagsOp.add, flag: MessageFlag.read,
523+
expected: {
524+
'anchor': 'oldest',
525+
'num_before': '0',
526+
'num_after': '20',
527+
'narrow': jsonEncode([]),
528+
'op': 'add',
529+
'flag': 'read',
530+
});
531+
});
532+
});
533+
534+
test('narrow uses resolveLegacyElements to encode', () {
535+
return FakeApiConnection.with_(zulipFeatureLevel: 176, (connection) async {
536+
connection.prepare(json: mkResult(foundOldest: true).toJson());
537+
await checkUpdateMessageFlagsForNarrow(connection,
538+
anchor: AnchorCode.oldest,
539+
numBefore: 0, numAfter: 20,
540+
narrow: [ApiNarrowDm([123, 234])],
541+
op: UpdateMessageFlagsOp.add, flag: MessageFlag.read,
542+
expected: {
543+
'anchor': 'oldest',
544+
'num_before': '0',
545+
'num_after': '20',
546+
'narrow': jsonEncode([
547+
{'operator': 'pm-with', 'operand': [123, 234]},
548+
]),
549+
'op': 'add',
550+
'flag': 'read',
551+
});
552+
});
553+
});
554+
555+
test('numeric anchor', () {
556+
return FakeApiConnection.with_((connection) async {
557+
connection.prepare(json: mkResult(foundOldest: false).toJson());
558+
await checkUpdateMessageFlagsForNarrow(connection,
559+
anchor: const NumericAnchor(42),
560+
numBefore: 0, numAfter: 20,
561+
narrow: const AllMessagesNarrow().apiEncode(),
562+
op: UpdateMessageFlagsOp.add, flag: MessageFlag.read,
563+
expected: {
564+
'anchor': '42',
565+
'num_before': '0',
566+
'num_after': '20',
567+
'narrow': jsonEncode([]),
568+
'op': 'add',
569+
'flag': 'read',
570+
});
571+
});
572+
});
573+
});
454574
}

0 commit comments

Comments
 (0)