@@ -7,6 +7,7 @@ import type { CrossRealmBot, User } from './modelTypes';
77import { apiPost } from './apiFetch' ;
88import { AvatarURL } from '../utils/avatar' ;
99import { ZulipVersion } from '../utils/zulipVersion' ;
10+ import { ServerTooOldError , kMinAllowedServerVersion } from './apiErrors' ;
1011
1112const transformUser = ( rawUser : { | ...User , avatar_url ?: string | null | } , realm : URL ) : User => {
1213 const { avatar_url : rawAvatarUrl , email } = rawUser ;
@@ -34,71 +35,82 @@ const transformCrossRealmBot = (
3435 } ;
3536} ;
3637
37- const transform = ( rawInitialData : RawInitialData , auth : Auth ) : InitialData => ( {
38- ...rawInitialData ,
39-
40- zulip_feature_level : rawInitialData . zulip_feature_level ?? 0 ,
41- zulip_version : new ZulipVersion ( rawInitialData . zulip_version ) ,
42-
43- // Transform the newer `realm_linkifiers` format, if present, to the
44- // older `realm_filters` format. We do the same transformation on
45- // 'realm_linkifiers' events.
46- // TODO(server-4.0): Switch to new format, if we haven't already;
47- // and drop conversion.
48- realm_filters : rawInitialData . realm_linkifiers
49- ? rawInitialData . realm_linkifiers . map ( ( { pattern, url_format, id } ) => [
50- pattern ,
51- url_format ,
52- id ,
53- ] )
54- : rawInitialData . realm_filters ,
55-
56- // In 5.0 (feature level 100), the representation the server sends for "no
57- // limit" changed from 0 to `null`.
58- //
59- // It's convenient to emulate Server 5.0's representation in our own data
60- // structures. To get a correct initial value, it's sufficient to coerce
61- // `0` to null here, without even looking at the server feature level.
62- // That's because, in addition to the documented change in 5.0, there was
63- // another: 0 became an invalid value, which means we don't have to
64- // anticipate servers 5.0+ using it to mean anything, such as "0 seconds":
65- // https://github.com/zulip/zulip/blob/b13bfa09c/zerver/lib/message.py#L1482.
66- //
67- // TODO(server-5.0) Remove this conditional.
68- realm_message_content_delete_limit_seconds :
69- rawInitialData . realm_message_content_delete_limit_seconds === 0
70- ? null
71- : rawInitialData . realm_message_content_delete_limit_seconds ,
72-
73- realm_users : rawInitialData . realm_users . map ( rawUser => transformUser ( rawUser , auth . realm ) ) ,
74- realm_non_active_users : rawInitialData . realm_non_active_users . map ( rawNonActiveUser =>
75- transformUser ( rawNonActiveUser , auth . realm ) ,
76- ) ,
77- cross_realm_bots : rawInitialData . cross_realm_bots . map ( rawCrossRealmBot =>
78- transformCrossRealmBot ( rawCrossRealmBot , auth . realm ) ,
79- ) ,
80-
81- // The doc says the field will be removed in a future release. So, while
82- // we're still consuming it, fill it in if missing, with instructions from
83- // the doc:
84- //
85- // > Its value will always equal
86- // > `can_create_public_streams || can_create_private_streams`.
87- //
88- // TODO(server-5.0): Only use `can_create_public_streams` and
89- // `can_create_private_streams`, and simplify this away.
90- can_create_streams :
91- rawInitialData . can_create_streams
92- ?? ( ( ) => {
93- const canCreatePublicStreams = rawInitialData . can_create_public_streams ;
94- const canCreatePrivateStreams = rawInitialData . can_create_private_streams ;
95- invariant (
96- canCreatePublicStreams != null && canCreatePrivateStreams != null ,
97- 'these are both present if can_create_streams is missing; see doc' ,
98- ) ;
99- return canCreatePublicStreams || canCreatePrivateStreams ;
100- } ) ( ) ,
101- } ) ;
38+ const transform = ( rawInitialData : RawInitialData , auth : Auth ) : InitialData => {
39+ // (Even ancient servers have `zulip_version` in the initial data.)
40+ const zulipVersion = new ZulipVersion ( rawInitialData . zulip_version ) ;
41+
42+ // Do this at the top, before we can accidentally trip on some later code
43+ // that's insensitive to ancient servers' behavior.
44+ if ( ! zulipVersion . isAtLeast ( kMinAllowedServerVersion ) ) {
45+ throw new ServerTooOldError ( zulipVersion ) ;
46+ }
47+
48+ return {
49+ ...rawInitialData ,
50+
51+ zulip_feature_level : rawInitialData . zulip_feature_level ?? 0 ,
52+ zulip_version : zulipVersion ,
53+
54+ // Transform the newer `realm_linkifiers` format, if present, to the
55+ // older `realm_filters` format. We do the same transformation on
56+ // 'realm_linkifiers' events.
57+ // TODO(server-4.0): Switch to new format, if we haven't already;
58+ // and drop conversion.
59+ realm_filters : rawInitialData . realm_linkifiers
60+ ? rawInitialData . realm_linkifiers . map ( ( { pattern, url_format, id } ) => [
61+ pattern ,
62+ url_format ,
63+ id ,
64+ ] )
65+ : rawInitialData . realm_filters ,
66+
67+ // In 5.0 (feature level 100), the representation the server sends for "no
68+ // limit" changed from 0 to `null`.
69+ //
70+ // It's convenient to emulate Server 5.0's representation in our own data
71+ // structures. To get a correct initial value, it's sufficient to coerce
72+ // `0` to null here, without even looking at the server feature level.
73+ // That's because, in addition to the documented change in 5.0, there was
74+ // another: 0 became an invalid value, which means we don't have to
75+ // anticipate servers 5.0+ using it to mean anything, such as "0 seconds":
76+ // https://github.com/zulip/zulip/blob/b13bfa09c/zerver/lib/message.py#L1482.
77+ //
78+ // TODO(server-5.0) Remove this conditional.
79+ realm_message_content_delete_limit_seconds :
80+ rawInitialData . realm_message_content_delete_limit_seconds === 0
81+ ? null
82+ : rawInitialData . realm_message_content_delete_limit_seconds ,
83+
84+ realm_users : rawInitialData . realm_users . map ( rawUser => transformUser ( rawUser , auth . realm ) ) ,
85+ realm_non_active_users : rawInitialData . realm_non_active_users . map ( rawNonActiveUser =>
86+ transformUser ( rawNonActiveUser , auth . realm ) ,
87+ ) ,
88+ cross_realm_bots : rawInitialData . cross_realm_bots . map ( rawCrossRealmBot =>
89+ transformCrossRealmBot ( rawCrossRealmBot , auth . realm ) ,
90+ ) ,
91+
92+ // The doc says the field will be removed in a future release. So, while
93+ // we're still consuming it, fill it in if missing, with instructions from
94+ // the doc:
95+ //
96+ // > Its value will always equal
97+ // > `can_create_public_streams || can_create_private_streams`.
98+ //
99+ // TODO(server-5.0): Only use `can_create_public_streams` and
100+ // `can_create_private_streams`, and simplify this away.
101+ can_create_streams :
102+ rawInitialData . can_create_streams
103+ ?? ( ( ) => {
104+ const canCreatePublicStreams = rawInitialData . can_create_public_streams ;
105+ const canCreatePrivateStreams = rawInitialData . can_create_private_streams ;
106+ invariant (
107+ canCreatePublicStreams != null && canCreatePrivateStreams != null ,
108+ 'these are both present if can_create_streams is missing; see doc' ,
109+ ) ;
110+ return canCreatePublicStreams || canCreatePrivateStreams ;
111+ } ) ( ) ,
112+ } ;
113+ } ;
102114
103115/** See https://zulip.com/api/register-queue */
104116export default async (
0 commit comments