@@ -7,6 +7,7 @@ import type { CrossRealmBot, User } from './modelTypes';
7
7
import { apiPost } from './apiFetch' ;
8
8
import { AvatarURL } from '../utils/avatar' ;
9
9
import { ZulipVersion } from '../utils/zulipVersion' ;
10
+ import { ServerTooOldError , kMinAllowedServerVersion } from './apiErrors' ;
10
11
11
12
const transformUser = ( rawUser : { | ...User , avatar_url ?: string | null | } , realm : URL ) : User => {
12
13
const { avatar_url : rawAvatarUrl , email } = rawUser ;
@@ -34,71 +35,82 @@ const transformCrossRealmBot = (
34
35
} ;
35
36
} ;
36
37
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
+ } ;
102
114
103
115
/** See https://zulip.com/api/register-queue */
104
116
export default async (
0 commit comments