Skip to content

Commit

Permalink
feat: supporting new analytics options, changed analytics algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
cloudinary-pkoniu committed Nov 15, 2023
1 parent 4d22af7 commit a08fdba
Show file tree
Hide file tree
Showing 22 changed files with 463 additions and 36 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ coverage
# contains temporary cloudianry_url for test accounts
tools/cloudinary_url.sh
package-lock.json
npm-debug.log
46 changes: 46 additions & 0 deletions lib-es5/utils/analytics/encodeVersion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';

var reverseVersion = require('./reverseVersion');
var stringPad = require('./stringPad');
var base64Map = require('../encoding/base64Map');

/**
* @private
* @description Encodes a semVer-like version string
* @param {string} semVer Input can be either x.y.z or x.y
* @return {string} A string built from 3 characters of the base64 table that encode the semVer
*/
module.exports = function (semVer) {
var strResult = '';

// support x.y or x.y.z by using 'parts' as a variable
var parts = semVer.split('.').length;
var paddedStringLength = parts * 6; // we pad to either 12 or 18 characters

// reverse (but don't mirror) the version. 1.5.15 -> 15.5.1
// Pad to two spaces, 15.5.1 -> 15.05.01
var paddedReversedSemver = reverseVersion(semVer);

// turn 15.05.01 to a string '150501' then to a number 150501
var num = parseInt(paddedReversedSemver.split('.').join(''));

// Represent as binary, add left padding to 12 or 18 characters.
// 150,501 -> 100100101111100101

var paddedBinary = num.toString(2);
paddedBinary = stringPad(paddedBinary, paddedStringLength, '0');

// Stop in case an invalid version number was provided
// paddedBinary must be built from sections of 6 bits
if (paddedBinary.length % 6 !== 0) {
throw 'Version must be smaller than 43.21.26)';
}

// turn every 6 bits into a character using the base64Map
paddedBinary.match(/.{1,6}/g).forEach(function (bitString) {
// console.log(bitString);
strResult += base64Map[bitString];
});

return strResult;
};
35 changes: 35 additions & 0 deletions lib-es5/utils/analytics/getSDKVersions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict';

var fs = require('fs');
var path = require('path');
var sdkCode = 'M'; // Constant per SDK

/**
* @description Gets the relevant versions of the SDK(package version, node version and sdkCode)
* @param {'default' | 'x.y.z' | 'x.y' | string} useSDKVersion Default uses package.json version
* @param {'default' | 'x.y.z' | 'x.y' | string} useNodeVersion Default uses process.versions.node
* @return {{sdkSemver:string, techVersion:string, sdkCode:string}} A map of relevant versions and codes
*/
function getSDKVersions() {
var useSDKVersion = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'default';
var useNodeVersion = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'default';

var pkgJSONFile = fs.readFileSync(path.join(__dirname, '../../../package.json'), 'utf-8');

// allow to pass a custom SDKVersion
var sdkSemver = useSDKVersion === 'default' ? JSON.parse(pkgJSONFile).version : useSDKVersion;

// allow to pass a custom techVersion (Node version)
var techVersion = useNodeVersion === 'default' ? process.versions.node : useNodeVersion;

var product = 'A';

return {
sdkSemver,
techVersion,
sdkCode,
product
};
}

module.exports = getSDKVersions;
71 changes: 71 additions & 0 deletions lib-es5/utils/analytics/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use strict';

var removePatchFromSemver = require('./removePatchFromSemver');
var encodeVersion = require('./encodeVersion');

/**
* @description Gets the SDK signature by encoding the SDK version and tech version
* @param {{
* [techVersion]:string,
* [sdkSemver]: string,
* [sdkCode]: string,
* [product]: string,
* [feature]: string
* }} analyticsOptions
* @return {string} sdkAnalyticsSignature
*/
function getSDKAnalyticsSignature() {
var analyticsOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

try {
var twoPartVersion = removePatchFromSemver(analyticsOptions.techVersion);
var encodedSDKVersion = encodeVersion(analyticsOptions.sdkSemver);
var encodedTechVersion = encodeVersion(twoPartVersion);
var featureCode = analyticsOptions.feature;
var SDKCode = analyticsOptions.sdkCode;
var product = analyticsOptions.product;
var algoVersion = 'B'; // The algo version is determined here, it should not be an argument

return `${algoVersion}${product}${SDKCode}${encodedSDKVersion}${encodedTechVersion}${featureCode}`;
} catch (e) {
// Either SDK or Node versions were unparsable
return 'E';
}
}

/**
* @description Gets the analyticsOptions from options - should include sdkSemver, techVersion, sdkCode, and feature
* @param options
* @returns {{sdkSemver: (string), sdkCode, product, feature: string, techVersion: (string)} || {}}
*/
function getAnalyticsOptions(options) {
var analyticsOptions = {
sdkSemver: options.sdkSemver,
techVersion: options.techVersion,
sdkCode: options.sdkCode,
product: options.product,
feature: '0'
};
if (options.urlAnalytics) {
if (options.accessibility) {
analyticsOptions.feature = 'D';
}
if (options.loading === 'lazy') {
analyticsOptions.feature = 'C';
}
if (options.responsive) {
analyticsOptions.feature = 'A';
}
if (options.placeholder) {
analyticsOptions.feature = 'B';
}
return analyticsOptions;
} else {
return {};
}
}

module.exports = {
getSDKAnalyticsSignature,
getAnalyticsOptions
};
11 changes: 11 additions & 0 deletions lib-es5/utils/analytics/removePatchFromSemver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

/**
* @description Removes patch version from the semver if it exists
* Turns x.y.z OR x.y into x.y
* @param {'x.y.z' || 'x.y' || string} semVerStr
*/
module.exports = function (semVerStr) {
var parts = semVerStr.split('.');
return `${parts[0]}.${parts[1]}`;
};
22 changes: 22 additions & 0 deletions lib-es5/utils/analytics/reverseVersion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

var stringPad = require('./stringPad');

/**
* @description A semVer like string, x.y.z or x.y is allowed
* Reverses the version positions, x.y.z turns to z.y.x
* Pads each segment with '0' so they have length of 2
* Example: 1.2.3 -> 03.02.01
* @param {string} semVer Input can be either x.y.z or x.y
* @return {string} in the form of zz.yy.xx (
*/
module.exports = function (semVer) {
if (semVer.split('.').length < 2) {
throw new Error('invalid semVer, must have at least two segments');
}

// Split by '.', reverse, create new array with padded values and concat it together
return semVer.split('.').reverse().map(function (segment) {
return stringPad(segment, 2, '0');
}).join('.');
};
24 changes: 24 additions & 0 deletions lib-es5/utils/analytics/stringPad.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';

function repeatStringNumTimes(string, times) {
var repeatedString = "";
while (times > 0) {
repeatedString += string;
times--;
}
return repeatedString;
}

module.exports = function (value, targetLength, padString) {
targetLength = targetLength >> 0; // truncate if number or convert non-number to 0;
padString = String(typeof padString !== 'undefined' ? padString : ' ');
if (value.length > targetLength) {
return String(value);
} else {
targetLength = targetLength - value.length;
if (targetLength > padString.length) {
padString += repeatStringNumTimes(padString, targetLength / padString.length);
}
return padString.slice(0, targetLength) + String(value);
}
};
22 changes: 22 additions & 0 deletions lib-es5/utils/encoding/base64Map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

var stringPad = require('../analytics/stringPad');

var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var num = 0;

/**
* Map of six-bit binary codes to Base64 characters
*/
var base64Map = {};

[].concat(_toConsumableArray(chars)).forEach(function (char) {
var key = num.toString(2);
key = stringPad(key, 6, '0');
base64Map[key] = char;
num++;
});

module.exports = base64Map;
1 change: 1 addition & 0 deletions lib-es5/utils/encoding/sdkAnalytics/getSDKVersions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
var fs = require('fs');
var path = require('path');
var sdkCode = 'M'; // Constant per SDK
// const packageJson = require('../../../package.json');

/**
* @description Gets the relevant versions of the SDK(package version, node version and sdkCode)
Expand Down
2 changes: 1 addition & 1 deletion lib-es5/utils/ensureOption.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function defaults(defaultOptions) {
} else if (typeof defaultValue !== 'undefined') {
value = defaultValue;
} else {
throw `Must supply ${name}`;
throw new Error(`Must supply ${name}`);
}

return value;
Expand Down
44 changes: 26 additions & 18 deletions lib-es5/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,17 @@ var ensurePresenceOf = require('./ensurePresenceOf');
var ensureOption = require('./ensureOption').defaults(config());
var entries = require('./entries');
var isRemoteUrl = require('./isRemoteUrl');
var getSDKVersions = require('./encoding/sdkAnalytics/getSDKVersions');
// const getSDKVersions = require('./encoding/sdkAnalytics/getSDKVersions');
// const {
// getAnalyticsOptions,
// getSDKAnalyticsSignature
// } = require('cloudinary-core').Util;

var _require$Util = require('cloudinary-core').Util,
getAnalyticsOptions = _require$Util.getAnalyticsOptions,
getSDKAnalyticsSignature = _require$Util.getSDKAnalyticsSignature;
var getSDKVersions = require('./analytics/getSDKVersions');

var _require2 = require('./analytics'),
getAnalyticsOptions = _require2.getAnalyticsOptions,
getSDKAnalyticsSignature = _require2.getSDKAnalyticsSignature;

exports = module.exports;
var utils = module.exports;
Expand Down Expand Up @@ -89,18 +95,18 @@ function getUserAgent() {
return isEmpty(utils.userPlatform) ? `${utils.USER_AGENT}` : `${utils.userPlatform} ${utils.USER_AGENT}`;
}

var _require2 = require('./consts'),
DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = _require2.DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION,
DEFAULT_POSTER_OPTIONS = _require2.DEFAULT_POSTER_OPTIONS,
DEFAULT_VIDEO_SOURCE_TYPES = _require2.DEFAULT_VIDEO_SOURCE_TYPES,
CONDITIONAL_OPERATORS = _require2.CONDITIONAL_OPERATORS,
PREDEFINED_VARS = _require2.PREDEFINED_VARS,
LAYER_KEYWORD_PARAMS = _require2.LAYER_KEYWORD_PARAMS,
TRANSFORMATION_PARAMS = _require2.TRANSFORMATION_PARAMS,
SIMPLE_PARAMS = _require2.SIMPLE_PARAMS,
UPLOAD_PREFIX = _require2.UPLOAD_PREFIX,
SUPPORTED_SIGNATURE_ALGORITHMS = _require2.SUPPORTED_SIGNATURE_ALGORITHMS,
DEFAULT_SIGNATURE_ALGORITHM = _require2.DEFAULT_SIGNATURE_ALGORITHM;
var _require3 = require('./consts'),
DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = _require3.DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION,
DEFAULT_POSTER_OPTIONS = _require3.DEFAULT_POSTER_OPTIONS,
DEFAULT_VIDEO_SOURCE_TYPES = _require3.DEFAULT_VIDEO_SOURCE_TYPES,
CONDITIONAL_OPERATORS = _require3.CONDITIONAL_OPERATORS,
PREDEFINED_VARS = _require3.PREDEFINED_VARS,
LAYER_KEYWORD_PARAMS = _require3.LAYER_KEYWORD_PARAMS,
TRANSFORMATION_PARAMS = _require3.TRANSFORMATION_PARAMS,
SIMPLE_PARAMS = _require3.SIMPLE_PARAMS,
UPLOAD_PREFIX = _require3.UPLOAD_PREFIX,
SUPPORTED_SIGNATURE_ALGORITHMS = _require3.SUPPORTED_SIGNATURE_ALGORITHMS,
DEFAULT_SIGNATURE_ALGORITHM = _require3.DEFAULT_SIGNATURE_ALGORITHM;

function textStyle(layer) {
var keywords = [];
Expand Down Expand Up @@ -970,12 +976,14 @@ function url(public_id) {
var _getSDKVersions = getSDKVersions(),
sdkCode = _getSDKVersions.sdkCode,
sdkSemver = _getSDKVersions.sdkSemver,
techVersion = _getSDKVersions.techVersion;
techVersion = _getSDKVersions.techVersion,
product = _getSDKVersions.product;

var sdkVersions = {
sdkCode: ensureOption(options, 'sdkCode', sdkCode),
sdkSemver: ensureOption(options, 'sdkSemver', sdkSemver),
techVersion: ensureOption(options, 'techVersion', techVersion)
techVersion: ensureOption(options, 'techVersion', techVersion),
product: ensureOption(options, 'product', product)
};

var analyticsOptions = getAnalyticsOptions(Object.assign({}, options, sdkVersions));
Expand Down
44 changes: 44 additions & 0 deletions lib/utils/analytics/encodeVersion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const reverseVersion = require('./reverseVersion');
const stringPad = require('./stringPad');
const base64Map = require('../encoding/base64Map');

/**
* @private
* @description Encodes a semVer-like version string
* @param {string} semVer Input can be either x.y.z or x.y
* @return {string} A string built from 3 characters of the base64 table that encode the semVer
*/
module.exports = (semVer) => {
let strResult = '';

// support x.y or x.y.z by using 'parts' as a variable
let parts = semVer.split('.').length;
let paddedStringLength = parts * 6; // we pad to either 12 or 18 characters

// reverse (but don't mirror) the version. 1.5.15 -> 15.5.1
// Pad to two spaces, 15.5.1 -> 15.05.01
let paddedReversedSemver = reverseVersion(semVer);

// turn 15.05.01 to a string '150501' then to a number 150501
let num = parseInt(paddedReversedSemver.split('.').join(''));

// Represent as binary, add left padding to 12 or 18 characters.
// 150,501 -> 100100101111100101

let paddedBinary = num.toString(2);
paddedBinary = stringPad(paddedBinary, paddedStringLength, '0');

// Stop in case an invalid version number was provided
// paddedBinary must be built from sections of 6 bits
if (paddedBinary.length % 6 !== 0) {
throw 'Version must be smaller than 43.21.26)';
}

// turn every 6 bits into a character using the base64Map
paddedBinary.match(/.{1,6}/g).forEach((bitString) => {
// console.log(bitString);
strResult += base64Map[bitString];
});

return strResult;
};
Loading

0 comments on commit a08fdba

Please sign in to comment.