Skip to content

Commit dfcc6ca

Browse files
committed
api: Add updateMessageFlags and updateMessageFlagsForNarrow routes
1 parent dfb1a9e commit dfcc6ca

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
@@ -430,7 +430,7 @@ sealed class Message {
430430
Map<String, dynamic> toJson();
431431
}
432432

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

lib/api/route/messages.dart

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

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).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).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+
UpdateMessageFlagsForNarrowResult 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 resolveDmElements 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)