@@ -25,6 +25,8 @@ import UserRouter from '../Routers/UsersRouter';
25
25
import DatabaseController from '../Controllers/DatabaseController' ;
26
26
import { isDeepStrictEqual } from 'util' ;
27
27
import deepcopy from 'deepcopy' ;
28
+ import RestQuery from '../RestQuery' ;
29
+ import { master as masterAuth } from '../Auth' ;
28
30
29
31
class ParseLiveQueryServer {
30
32
server : any ;
@@ -241,6 +243,7 @@ class ParseLiveQueryServer {
241
243
}
242
244
if ( res . object && typeof res . object . toJSON === 'function' ) {
243
245
deletedParseObject = toJSONwithObjects ( res . object , res . object . className || className ) ;
246
+ deletedParseObject = await this . _applyInclude ( client , requestId , deletedParseObject ) ;
244
247
}
245
248
await this . _filterSensitiveData (
246
249
classLevelPermissions ,
@@ -393,12 +396,11 @@ class ParseLiveQueryServer {
393
396
}
394
397
if ( res . object && typeof res . object . toJSON === 'function' ) {
395
398
currentParseObject = toJSONwithObjects ( res . object , res . object . className || className ) ;
399
+ currentParseObject = await this . _applyInclude ( client , requestId , currentParseObject ) ;
396
400
}
397
401
if ( res . original && typeof res . original . toJSON === 'function' ) {
398
- originalParseObject = toJSONwithObjects (
399
- res . original ,
400
- res . original . className || className
401
- ) ;
402
+ originalParseObject = toJSONwithObjects ( res . original , res . original . className || className ) ;
403
+ originalParseObject = await this . _applyInclude ( client , requestId , originalParseObject ) ;
402
404
}
403
405
await this . _filterSensitiveData (
404
406
classLevelPermissions ,
@@ -553,7 +555,7 @@ class ParseLiveQueryServer {
553
555
}
554
556
}
555
557
556
- getAuthForSessionToken ( sessionToken ?: string ) : Promise < { auth ?: Auth , userId ?: string } > {
558
+ getAuthForSessionToken ( sessionToken ?: string ) : Promise < { auth ?: Auth ; userId ?: string } > {
557
559
if ( ! sessionToken ) {
558
560
return Promise . resolve ( { } ) ;
559
561
}
@@ -674,6 +676,24 @@ class ParseLiveQueryServer {
674
676
res . original = filter ( res . original ) ;
675
677
}
676
678
679
+ async _applyInclude ( client : any , requestId : number , object : any ) {
680
+ const subscriptionInfo = client . getSubscriptionInfo ( requestId ) ;
681
+ if ( ! object || ! subscriptionInfo ) {
682
+ return object ;
683
+ }
684
+ const include = subscriptionInfo . include ;
685
+ if ( ! include || include . length === 0 ) {
686
+ return object ;
687
+ }
688
+ const restOptions : any = { } ;
689
+ if ( subscriptionInfo . keys ) {
690
+ restOptions . keys = Array . isArray ( subscriptionInfo . keys )
691
+ ? subscriptionInfo . keys . join ( ',' )
692
+ : subscriptionInfo . keys ;
693
+ }
694
+ return includeObject ( this . config , object , include , { } , restOptions , masterAuth ( this . config ) ) ;
695
+ }
696
+
677
697
_getCLPOperation ( query : any ) {
678
698
return typeof query === 'object' &&
679
699
Object . keys ( query ) . length == 1 &&
@@ -933,6 +953,11 @@ class ParseLiveQueryServer {
933
953
? request . query . keys
934
954
: request . query . keys . split ( ',' ) ;
935
955
}
956
+ if ( request . query . include ) {
957
+ subscriptionInfo . include = Array . isArray ( request . query . include )
958
+ ? request . query . include
959
+ : request . query . include . split ( ',' ) ;
960
+ }
936
961
if ( request . query . watch ) {
937
962
subscriptionInfo . watch = request . query . watch ;
938
963
}
@@ -1056,6 +1081,192 @@ class ParseLiveQueryServer {
1056
1081
`Delete client: ${ parseWebsocket . clientId } | subscription: ${ request . requestId } `
1057
1082
) ;
1058
1083
}
1084
+
1085
+ async includePath (
1086
+ config : any ,
1087
+ auth : any ,
1088
+ response : any ,
1089
+ path : Array < string > ,
1090
+ context : any ,
1091
+ restOptions : any = { } ,
1092
+ ) {
1093
+ const pointers = this . findPointers ( response . results , path ) ;
1094
+ if ( pointers . length === 0 ) {
1095
+ return response ;
1096
+ }
1097
+ const pointersHash : any = { } ;
1098
+ for ( const pointer of pointers ) {
1099
+ if ( ! pointer ) {
1100
+ continue ;
1101
+ }
1102
+ const className = pointer . className ;
1103
+ if ( className ) {
1104
+ pointersHash [ className ] = pointersHash [ className ] || new Set ( ) ;
1105
+ pointersHash [ className ] . add ( pointer . objectId ) ;
1106
+ }
1107
+ }
1108
+ const includeRestOptions : any = { } ;
1109
+ if ( restOptions . keys ) {
1110
+ const keys = new Set ( restOptions . keys . split ( ',' ) ) ;
1111
+ const keySet = Array . from ( keys ) . reduce ( ( set , key ) => {
1112
+ const keyPath = key . split ( '.' ) ;
1113
+ let i = 0 ;
1114
+ for ( ; i < path . length ; i ++ ) {
1115
+ if ( path [ i ] != keyPath [ i ] ) {
1116
+ return set ;
1117
+ }
1118
+ }
1119
+ if ( i < keyPath . length ) {
1120
+ set . add ( keyPath [ i ] ) ;
1121
+ }
1122
+ return set ;
1123
+ } , new Set < string > ( ) ) ;
1124
+ if ( keySet . size > 0 ) {
1125
+ includeRestOptions . keys = Array . from ( keySet ) . join ( ',' ) ;
1126
+ }
1127
+ }
1128
+
1129
+ if ( restOptions . excludeKeys ) {
1130
+ const excludeKeys = new Set ( restOptions . excludeKeys . split ( ',' ) ) ;
1131
+ const excludeKeySet = Array . from ( excludeKeys ) . reduce ( ( set , key ) => {
1132
+ const keyPath = key . split ( '.' ) ;
1133
+ let i = 0 ;
1134
+ for ( ; i < path . length ; i ++ ) {
1135
+ if ( path [ i ] != keyPath [ i ] ) {
1136
+ return set ;
1137
+ }
1138
+ }
1139
+ if ( i == keyPath . length - 1 ) {
1140
+ set . add ( keyPath [ i ] ) ;
1141
+ }
1142
+ return set ;
1143
+ } , new Set < string > ( ) ) ;
1144
+ if ( excludeKeySet . size > 0 ) {
1145
+ includeRestOptions . excludeKeys = Array . from ( excludeKeySet ) . join ( ',' ) ;
1146
+ }
1147
+ }
1148
+
1149
+ if ( restOptions . includeReadPreference ) {
1150
+ includeRestOptions . readPreference = restOptions . includeReadPreference ;
1151
+ includeRestOptions . includeReadPreference = restOptions . includeReadPreference ;
1152
+ } else if ( restOptions . readPreference ) {
1153
+ includeRestOptions . readPreference = restOptions . readPreference ;
1154
+ }
1155
+
1156
+ const queryPromises = Object . keys ( pointersHash ) . map ( async className => {
1157
+ const objectIds = Array . from ( pointersHash [ className ] ) ;
1158
+ let where ;
1159
+ if ( objectIds . length === 1 ) {
1160
+ where = { objectId : objectIds [ 0 ] } ;
1161
+ } else {
1162
+ where = { objectId : { $in : objectIds } } ;
1163
+ }
1164
+ const query = await RestQuery ( {
1165
+ method : objectIds . length === 1 ? RestQuery . Method . get : RestQuery . Method . find ,
1166
+ config,
1167
+ auth,
1168
+ className,
1169
+ restWhere : where ,
1170
+ restOptions : includeRestOptions ,
1171
+ context : context ,
1172
+ } ) ;
1173
+ return query . execute ( { op : 'get' } ) . then ( results => {
1174
+ results . className = className ;
1175
+ return Promise . resolve ( results ) ;
1176
+ } ) ;
1177
+ } ) ;
1178
+
1179
+ const responses = await Promise . all ( queryPromises ) ;
1180
+ const replace = responses . reduce ( ( acc , includeResponse ) => {
1181
+ for ( const obj of includeResponse . results ) {
1182
+ obj . __type = 'Object' ;
1183
+ obj . className = includeResponse . className ;
1184
+ if ( obj . className === '_User' && ! auth . isMaster ) {
1185
+ delete obj . sessionToken ;
1186
+ delete obj . authData ;
1187
+ }
1188
+ acc [ obj . objectId ] = obj ;
1189
+ }
1190
+ return acc ;
1191
+ } , { } as any ) ;
1192
+
1193
+ const resp : any = {
1194
+ results : this . replacePointers ( response . results , path , replace ) ,
1195
+ } ;
1196
+ if ( response . count ) {
1197
+ resp . count = response . count ;
1198
+ }
1199
+ return resp ;
1200
+ }
1201
+
1202
+ findPointers ( object : any , path : Array < string > ) : any [ ] {
1203
+ if ( object instanceof Array ) {
1204
+ return object . map ( x => this . findPointers ( x , path ) ) . flat ( ) ;
1205
+ }
1206
+ if ( typeof object !== 'object' || ! object ) {
1207
+ return [ ] ;
1208
+ }
1209
+ if ( path . length === 0 ) {
1210
+ if ( object === null || object . __type === 'Pointer' ) {
1211
+ return [ object ] ;
1212
+ }
1213
+ return [ ] ;
1214
+ }
1215
+ const subObject = object [ path [ 0 ] ] ;
1216
+ if ( ! subObject ) {
1217
+ return [ ] ;
1218
+ }
1219
+ return this . findPointers ( subObject , path . slice ( 1 ) ) ;
1220
+ }
1221
+
1222
+ replacePointers ( object : any , path : Array < string > , replace : any ) : any {
1223
+ if ( object instanceof Array ) {
1224
+ return object
1225
+ . map ( obj => this . replacePointers ( obj , path , replace ) )
1226
+ . filter ( obj => typeof obj !== 'undefined' ) ;
1227
+ }
1228
+ if ( typeof object !== 'object' || ! object ) {
1229
+ return object ;
1230
+ }
1231
+ if ( path . length === 0 ) {
1232
+ if ( object && object . __type === 'Pointer' ) {
1233
+ return replace [ object . objectId ] ;
1234
+ }
1235
+ return object ;
1236
+ }
1237
+ const subObject = object [ path [ 0 ] ] ;
1238
+ if ( ! subObject ) {
1239
+ return object ;
1240
+ }
1241
+ const newSub = this . replacePointers ( subObject , path . slice ( 1 ) , replace ) ;
1242
+ const answer : any = { } ;
1243
+ for ( const key in object ) {
1244
+ if ( key === path [ 0 ] ) {
1245
+ answer [ key ] = newSub ;
1246
+ } else {
1247
+ answer [ key ] = object [ key ] ;
1248
+ }
1249
+ }
1250
+ return answer ;
1251
+ }
1252
+
1253
+ async includeObject (
1254
+ config : any ,
1255
+ object : any ,
1256
+ include : Array < string > ,
1257
+ context : any ,
1258
+ restOptions : any ,
1259
+ auth : any
1260
+ ) {
1261
+ if ( ! include || include . length === 0 ) {
1262
+ return object ;
1263
+ }
1264
+ let response = { results : [ object ] } as any ;
1265
+ for ( const path of include ) {
1266
+ response = await this . includePath ( config , auth , response , path . split ( '.' ) , context , restOptions ) ;
1267
+ }
1268
+ return response . results [ 0 ] ;
1269
+ }
1059
1270
}
1060
1271
1061
1272
export { ParseLiveQueryServer } ;
0 commit comments