@@ -25,6 +25,8 @@ import UserRouter from '../Routers/UsersRouter';
2525import DatabaseController from '../Controllers/DatabaseController' ;
2626import { isDeepStrictEqual } from 'util' ;
2727import deepcopy from 'deepcopy' ;
28+ import RestQuery from '../RestQuery' ;
29+ import { master as masterAuth } from '../Auth' ;
2830
2931class ParseLiveQueryServer {
3032 server : any ;
@@ -241,6 +243,7 @@ class ParseLiveQueryServer {
241243 }
242244 if ( res . object && typeof res . object . toJSON === 'function' ) {
243245 deletedParseObject = toJSONwithObjects ( res . object , res . object . className || className ) ;
246+ deletedParseObject = await this . _applyInclude ( client , requestId , deletedParseObject ) ;
244247 }
245248 await this . _filterSensitiveData (
246249 classLevelPermissions ,
@@ -393,12 +396,11 @@ class ParseLiveQueryServer {
393396 }
394397 if ( res . object && typeof res . object . toJSON === 'function' ) {
395398 currentParseObject = toJSONwithObjects ( res . object , res . object . className || className ) ;
399+ currentParseObject = await this . _applyInclude ( client , requestId , currentParseObject ) ;
396400 }
397401 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 ) ;
402404 }
403405 await this . _filterSensitiveData (
404406 classLevelPermissions ,
@@ -553,7 +555,7 @@ class ParseLiveQueryServer {
553555 }
554556 }
555557
556- getAuthForSessionToken ( sessionToken ?: string ) : Promise < { auth ?: Auth , userId ?: string } > {
558+ getAuthForSessionToken ( sessionToken ?: string ) : Promise < { auth ?: Auth ; userId ?: string } > {
557559 if ( ! sessionToken ) {
558560 return Promise . resolve ( { } ) ;
559561 }
@@ -674,6 +676,24 @@ class ParseLiveQueryServer {
674676 res . original = filter ( res . original ) ;
675677 }
676678
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+
677697 _getCLPOperation ( query : any ) {
678698 return typeof query === 'object' &&
679699 Object . keys ( query ) . length == 1 &&
@@ -933,6 +953,11 @@ class ParseLiveQueryServer {
933953 ? request . query . keys
934954 : request . query . keys . split ( ',' ) ;
935955 }
956+ if ( request . query . include ) {
957+ subscriptionInfo . include = Array . isArray ( request . query . include )
958+ ? request . query . include
959+ : request . query . include . split ( ',' ) ;
960+ }
936961 if ( request . query . watch ) {
937962 subscriptionInfo . watch = request . query . watch ;
938963 }
@@ -1056,6 +1081,192 @@ class ParseLiveQueryServer {
10561081 `Delete client: ${ parseWebsocket . clientId } | subscription: ${ request . requestId } `
10571082 ) ;
10581083 }
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+ }
10591270}
10601271
10611272export { ParseLiveQueryServer } ;
0 commit comments