2323var os = require ( "os" ) ;
2424var http = require ( "http" ) ;
2525var https = require ( "https" ) ;
26+ var fs = require ( "fs" ) ;
27+ var path = require ( "path" ) ;
2628var cluster = require ( "cluster" ) ;
2729var cc = require ( "./countly-common" ) ;
2830var Bulk = require ( "./countly-bulk" ) ;
@@ -33,7 +35,7 @@ Countly.StorageTypes = cc.storageTypeEnums;
3335Countly . DeviceIdType = cc . deviceIdTypeEnums ;
3436Countly . Bulk = Bulk ;
3537( function ( ) {
36- var SDK_VERSION = "24.10.2 " ;
38+ var SDK_VERSION = "24.10.3 " ;
3739 var SDK_NAME = "javascript_native_nodejs" ;
3840
3941 var inited = false ;
@@ -828,7 +830,8 @@ Countly.Bulk = Bulk;
828830 * @param {string= } user.organization - user's organization or company
829831 * @param {string= } user.phone - user's phone number
830832 * @param {string= } user.picture - url to user's picture
831- * @param {string= } user.gender - M value for male and F value for femail
833+ * @param {string= } user.picturePath - local path to user's picture, if 'user.picture' is set this will be ignored
834+ * @param {string= } user.gender - M value for male and F value for female
832835 * @param {number= } user.byear - user's birth year used to calculate current age
833836 * @param {Object= } user.custom - object with custom key value properties you want to save with user
834837 * */
@@ -845,7 +848,12 @@ Countly.Bulk = Bulk;
845848 user . gender = cc . truncateSingleValue ( user . gender , Countly . maxValueSize , "user_details" , Countly . debug ) ;
846849 user . byear = cc . truncateSingleValue ( user . byear , Countly . maxValueSize , "user_details" , Countly . debug ) ;
847850 user . custom = cc . truncateObject ( user . custom , Countly . maxKeyLength , Countly . maxValueSize , Countly . maxSegmentationValues , "user_details" ) ;
848- toRequestQueue ( { user_details : JSON . stringify ( cc . getProperties ( user , props ) ) } ) ;
851+ var request = { user_details : JSON . stringify ( cc . getProperties ( user , props ) ) } ;
852+ if ( ! user . picture && user . picturePath ) {
853+ cc . log ( cc . logLevelEnums . INFO , "user_details, Picture is not set but picturePath is set, will try to upload picture from path." ) ;
854+ request . picturePath = user . picturePath ;
855+ }
856+ toRequestQueue ( request ) ;
849857 }
850858 } ;
851859
@@ -1635,26 +1643,78 @@ Countly.Bulk = Bulk;
16351643 isBroad = isBroad || false ;
16361644 cc . log ( cc . logLevelEnums . INFO , `makeRequest, Sending ${ info } HTTP request` ) ;
16371645 var serverOptions = parseUrl ( url ) ;
1638- var data = prepareParams ( params ) ;
1646+ var data ;
16391647 var method = "GET" ;
16401648 var options = {
16411649 host : serverOptions . host ,
16421650 port : serverOptions . port ,
1643- path : ` ${ api } ? ${ data } ` ,
1651+ path : api ,
16441652 method : "GET" ,
16451653 } ;
16461654
1647- if ( data . length >= 2000 || Countly . force_post ) {
1648- method = "POST" ;
1655+ if ( params && params . picturePath ) {
1656+ try {
1657+ var filePath = params . picturePath ;
1658+ var fileName = path . basename ( filePath ) ;
1659+ var ext = ( fileName . split ( '.' ) . pop ( ) || '' ) . toLowerCase ( ) ;
1660+ var contentType = 'application/octet-stream' ;
1661+ if ( ext === 'png' ) contentType = 'image/png' ;
1662+ else if ( ext === 'jpg' || ext === 'jpeg' ) contentType = 'image/jpeg' ;
1663+ else if ( ext === 'gif' ) contentType = 'image/gif' ;
1664+
1665+ var boundary = 'FormBoundary' + Math . random ( ) . toString ( 16 ) . slice ( 2 ) ;
1666+ var bodyParts = [ ] ;
1667+
1668+ for ( var p in params ) {
1669+ if ( ! Object . prototype . hasOwnProperty . call ( params , p ) ) continue ;
1670+ if ( p === 'picturePath' ) continue ;
1671+ var value = params [ p ] ;
1672+ bodyParts . push ( Buffer . from ( `--${ boundary } \r\n` ) ) ;
1673+ bodyParts . push ( Buffer . from ( `Content-Disposition: form-data; name="${ p } "\r\n\r\n` ) ) ;
1674+ bodyParts . push ( Buffer . from ( String ( value ) ) ) ;
1675+ bodyParts . push ( Buffer . from ( '\r\n' ) ) ;
1676+ }
1677+
1678+ bodyParts . push ( Buffer . from ( `--${ boundary } \r\n` ) ) ;
1679+ bodyParts . push ( Buffer . from ( `Content-Disposition: form-data; name="user_picture"; filename="${ fileName } "\r\n` ) ) ;
1680+ bodyParts . push ( Buffer . from ( `Content-Type: ${ contentType } \r\n\r\n` ) ) ;
1681+ var fileBuffer = fs . readFileSync ( filePath ) ;
1682+ bodyParts . push ( fileBuffer ) ;
1683+ bodyParts . push ( Buffer . from ( '\r\n' ) ) ;
1684+
1685+ bodyParts . push ( Buffer . from ( `--${ boundary } --\r\n` ) ) ;
1686+
1687+ data = Buffer . concat ( bodyParts ) ;
1688+ method = "POST" ;
1689+ options . method = "POST" ;
1690+ options . path = api ;
1691+ options . headers = options . headers || { } ;
1692+ options . headers [ 'Content-Type' ] = 'multipart/form-data; boundary=' + boundary ;
1693+ options . headers [ 'Content-Length' ] = data . length ;
1694+ }
1695+ catch ( e ) {
1696+ cc . log ( cc . logLevelEnums . ERROR , `makeRequest, Failed preparing picture upload: [${ e } ]. Falling back to sending request without image.` ) ;
1697+ // Remove picturePath to continue with regular request
1698+ delete params . picturePath ;
1699+ }
16491700 }
1701+
1702+ if ( ! data ) {
1703+ data = prepareParams ( params ) ;
1704+ options . path = `${ api } ?${ data } ` ;
16501705
1651- if ( method === "POST" ) {
1652- options . method = "POST" ;
1653- options . path = api ;
1654- options . headers = {
1655- "Content-Type" : "application/x-www-form-urlencoded" ,
1656- "Content-Length" : Buffer . byteLength ( data ) ,
1657- } ;
1706+ if ( data . length >= 2000 || Countly . force_post ) {
1707+ method = "POST" ;
1708+ }
1709+
1710+ if ( method === "POST" ) {
1711+ options . method = "POST" ;
1712+ options . path = api ;
1713+ options . headers = {
1714+ "Content-Type" : "application/x-www-form-urlencoded" ,
1715+ "Content-Length" : Buffer . byteLength ( data ) ,
1716+ } ;
1717+ }
16581718 }
16591719
16601720 if ( typeof Countly . http_options === "function" ) {
0 commit comments