Skip to content

Commit c9d4e82

Browse files
committed
Picture upload
1 parent c6bd01d commit c9d4e82

9 files changed

Lines changed: 101 additions & 30 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 24.10.3
2+
- Added support for uploading user images by providing path to the local image using `picturePath` parameter in `user_details` method (non-bulk)
3+
- Reduced SDK log verbosity
4+
15
## 24.10.2
26
- Added timezone support for server
37

examples/bulk_import_example.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ server.start(function() {
2424
});
2525

2626

27-
var user = server.add_user({device_id: "my_device_id"});
27+
var user = server.add_user({ device_id: "my_device_id_1" });
28+
var user2 = server.add_user({ device_id: "my_device_id_2" });
2829

2930
user.begin_session({_os: "Android", _os_version: "7", _app_version: "1.0"}, 240, 1500645060)
3031
.add_event({key: "Test1", timestamp: 1500645060})
@@ -44,4 +45,10 @@ user.begin_session({_os: "Android", _os_version: "7", _app_version: "1.0"}, 240,
4445
.custom_min("min", 10)
4546
.custom_push("check", "a")
4647
.custom_push_unique("tags", "morning")
47-
.custom_save();
48+
.custom_save();
49+
50+
user2.begin_session({ _os: "iOS", _os_version: "10", _app_version: "1.0" }, 300, 1500645060)
51+
.add_event({ key: "Test1", timestamp: 1500645060 })
52+
.add_event({ key: "Test2", timestamp: 1500645060 })
53+
.add_event({ key: "Test3", timestamp: 1500645060 })
54+
.user_details({ name: "Test user2", email: "test2@test.test" })

examples/example.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,22 @@ setTimeout(function() {
2727
"dur": 1000,
2828
"segmentation": {
2929
"app_version": "1.0",
30-
"country": "Turkey"
30+
"country": "GB"
3131
}
3232
});
3333
}, 5000);
3434

3535
setTimeout(function() {
3636
Countly.user_details({
37-
"name": "Arturs Sosins",
38-
"username": "ar2rsawseen",
37+
"name": "User name",
38+
"username": "User nickname",
3939
"email": "test@test.com",
4040
"organization": "Countly",
41-
"phone": "+37112345678",
42-
//Web URL to picture
43-
"picture": "https://pbs.twimg.com/profile_images/1442562237/012_n_400x400.jpg",
41+
"phone": "+37144345678",
42+
// "picture": "http://www.gravatar.com/avatar/9b8f4f4c3b9b0f2e7f5f4e4e4e4e4e4e",
43+
"picturePath": "./logo.png",
4444
"gender": "M",
45-
"byear": 1987, //birth year
45+
"byear": 1987,
4646
"custom": {
4747
"key1": "value1",
4848
"key2": "value2",
@@ -73,7 +73,7 @@ setTimeout(function() {
7373
"count": 1,
7474
"segmentation": {
7575
"app_version": "1.0",
76-
"country": "Turkey"
76+
"country": "GB"
7777
}
7878
});
7979
}, 50000);

examples/logo.png

2.45 KB
Loading

lib/countly-bulk.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ CountlyBulk.StorageTypes = cc.storageTypeEnums;
5959
* });
6060
*/
6161
function CountlyBulk(conf) {
62-
var SDK_VERSION = "24.10.2";
62+
var SDK_VERSION = "24.10.3";
6363
var SDK_NAME = "javascript_native_nodejs_bulk";
6464

6565
var empty_queue_callback = null;
@@ -197,7 +197,7 @@ function CountlyBulk(conf) {
197197
query.hour = query.hour || date.getHours();
198198
query.dow = query.dow || date.getDay();
199199
query.tz = query.tz || -date.getTimezoneOffset();
200-
cc.log(cc.logLevelEnums.INFO, "CountlyBulk add_bulk_request, adding the request into queue.");
200+
cc.log(cc.logLevelEnums.INFO, "CountlyBulk add_bulk_request, adding the request into queue:["+ JSON.stringify(query) + "]");
201201
requestQueue.push(query);
202202
}
203203
CountlyStorage.storeSet("cly_req_queue", requestQueue);

lib/countly-common.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ var cc = {
245245
isResponseValidBroad: function isResponseValidBroad(statusCode, str) {
246246
// status code and response format check
247247
if (!(statusCode >= 200 && statusCode < 300)) {
248-
this.log(this.logLevelEnums.ERROR, `isResponseValidBroad, The server status code is not within the expected range: [${statusCode}]`);
248+
this.log(this.logLevelEnums.ERROR, `isResponseValidBroad, The server status code is not within the expected range: [${statusCode}] with: [${str}]`);
249249
return false;
250250
}
251251

lib/countly-storage.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const memoryStorage = {
5757
*/
5858
storeSet: function(key, value, callback) {
5959
if (key) {
60-
cc.log(cc.logLevelEnums.DEBUG, `storeSet, Setting key: [${key}] & value: [${value}]!`);
60+
// cc.log(cc.logLevelEnums.VERBOSE, `storeSet, Setting key: [${key}] & value: [${value}]!`);
6161
__cache[key] = value;
6262
if (typeof callback === "function") {
6363
callback(null);
@@ -74,7 +74,7 @@ const memoryStorage = {
7474
* @returns {varies} value for the key
7575
*/
7676
storeGet: function(key, def) {
77-
cc.log(cc.logLevelEnums.DEBUG, `storeGet, Fetching item from memory with key: [${key}].`);
77+
// cc.log(cc.logLevelEnums.VERBOSE, `storeGet, Fetching item from memory with key: [${key}].`);
7878
return typeof __cache[key] !== "undefined" ? __cache[key] : def;
7979
},
8080
/**

lib/countly.js

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
var os = require("os");
2424
var http = require("http");
2525
var https = require("https");
26+
var fs = require("fs");
27+
var path = require("path");
2628
var cluster = require("cluster");
2729
var cc = require("./countly-common");
2830
var Bulk = require("./countly-bulk");
@@ -33,7 +35,7 @@ Countly.StorageTypes = cc.storageTypeEnums;
3335
Countly.DeviceIdType = cc.deviceIdTypeEnums;
3436
Countly.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") {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "countly-sdk-nodejs",
3-
"version": "24.10.2",
3+
"version": "24.10.3",
44
"description": "Countly NodeJS SDK",
55
"main": "lib/countly.js",
66
"directories": {

0 commit comments

Comments
 (0)