Skip to content

Analytics algo d #589

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions __TESTS_BUNDLE_SIZE__/bundleSizeTestCases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import importFromPackage from "./utils/stringGenerators/importFromPackage";
const bundleSizeTestCases:ITestCase[] = [
{
name: 'Tests CloudinaryImage with Resize',
sizeLimitInKB: 27,
sizeLimitInKB: 28,
importsArray: [
importFromDist('assets/CloudinaryImage', 'CloudinaryImage'),
importFromDist('instance/Cloudinary', 'Cloudinary'),
Expand All @@ -24,7 +24,7 @@ const bundleSizeTestCases:ITestCase[] = [
},
{
name: 'Tests CloudinaryImage with Resize and Adjust',
sizeLimitInKB: 31,
sizeLimitInKB: 32,
importsArray: [
importFromDist('assets/CloudinaryImage', 'CloudinaryImage'),
importFromDist('instance/Cloudinary', 'Cloudinary'),
Expand All @@ -34,7 +34,7 @@ const bundleSizeTestCases:ITestCase[] = [
},
{
name: 'Tests CloudinaryImage with Resize, Adjust and Border',
sizeLimitInKB: 33,
sizeLimitInKB: 34,
importsArray: [
importFromDist('assets/CloudinaryImage', 'CloudinaryImage'),
importFromDist('instance/Cloudinary', 'Cloudinary'),
Expand All @@ -45,7 +45,7 @@ const bundleSizeTestCases:ITestCase[] = [
},
{
name: 'Tests CloudinaryImage image with Resize, adjust and delivery',
sizeLimitInKB: 33,
sizeLimitInKB: 34,
importsArray: [
importFromDist('assets/CloudinaryImage', 'CloudinaryImage'),
importFromDist('instance/Cloudinary', 'Cloudinary'),
Expand All @@ -56,7 +56,7 @@ const bundleSizeTestCases:ITestCase[] = [
},
{
name: 'Tests Overlay imports',
sizeLimitInKB: 30,
sizeLimitInKB: 31,
importsArray: [
importFromDist('assets/CloudinaryImage', 'CloudinaryImage'),
importFromDist('actions/overlay', 'Overlay'),
Expand Down
32 changes: 16 additions & 16 deletions __TESTS__/unit/analytics/analytics.browser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ describe('Add analytics to a URL from the browser', () => {
}
});

// BATAAB{NODE_VERSION}0
// BATAAB{AA}0 -> we expect nodeVersion to be 0.0.0 in browser (Since it's missing)
// expect BATAABAA0
expect(url).toContain('sample?_a=BATAABAA0'); // we shouldn't have a query param at all
// DATAABAAZ{NODE_VERSION}0
// DATAABAAZ{AA}0 -> we expect nodeVersion to be 0.0.0 in browser (Since it's missing)
// expect DATAABAAZAA0
expect(url).toContain('sample?_a=DATAABAAZAA0'); // we shouldn't have a query param at all
});

it('Uses default techVersion 0.0.0 when in browser for image with file extension', () => {
Expand All @@ -28,10 +28,10 @@ describe('Add analytics to a URL from the browser', () => {
}
});

// BATAAB{NODE_VERSION}0
// BATAAB{AA}0 -> we expect nodeVersion to be 0.0.0 in browser (Since it's missing)
// expect BATAABAA0
expect(url).toContain('sample.jpg?_a=BATAABAA0'); // we shouldn't have a query param at all
// DATAABAAZ{NODE_VERSION}0
// DATAABAAZ{AA}0 -> we expect nodeVersion to be 0.0.0 in browser (Since it's missing)
// expect DATAABAAZAA0
expect(url).toContain('sample.jpg?_a=DATAABAAZAA0'); // we shouldn't have a query param at all
});

it('Uses default techVersion 0.0.0 when in browser for video', () => {
Expand All @@ -42,10 +42,10 @@ describe('Add analytics to a URL from the browser', () => {
}
});

// BATAAB{NODE_VERSION}0
// BATAAB{AA}0 -> we expect nodeVersion to be 0.0.0 in browser (Since it's missing)
// expect BATAABAA0
expect(url).toContain('sample?_a=BATAABAA0'); // we shouldn't have a query param at all
// DATAABAAZ{NODE_VERSION}0
// DATAABAAZ{AA}0 -> we expect nodeVersion to be 0.0.0 in browser (Since it's missing)
// expect DATAABAAZAA0
expect(url).toContain('sample?_a=DATAABAAZAA0'); // we shouldn't have a query param at all
});


Expand All @@ -57,9 +57,9 @@ describe('Add analytics to a URL from the browser', () => {
}
});

// BATAAB{NODE_VERSION}0
// BATAAB{AA}0 -> we expect nodeVersion to be 0.0.0 in browser (Since it's missing)
// expect BATAABAA0
expect(url).toContain('sample.webm?_a=BATAABAA0'); // we shouldn't have a query param at all
// DATAABAAZ{NODE_VERSION}0
// DATAABAAZ{AA}0 -> we expect nodeVersion to be 0.0.0 in browser (Since it's missing)
// expect DATAABAAZAA0
expect(url).toContain('sample.webm?_a=DATAABAAZAA0'); // we shouldn't have a query param at all
});
});
36 changes: 31 additions & 5 deletions __TESTS__/unit/analytics/analytics.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('Add analytics to a regular URL', () => {
techVersion: '12.0.0',
accessibility: true
}
})).toContain('?_a=BAZAlhAMD');
})).toContain('?_a=DAZAlhAMZAAD');
});

it('Test lazyload feature value', () => {
Expand All @@ -59,7 +59,7 @@ describe('Add analytics to a regular URL', () => {
techVersion: '12.0.0',
lazyload: true
}
})).toContain('?_a=BAZAlhAMC');
})).toContain('?_a=DAZAlhAMZAAC');
});

it('Test responsive feature value', () => {
Expand All @@ -75,7 +75,7 @@ describe('Add analytics to a regular URL', () => {
techVersion: '12.0.0',
responsive: true
}
})).toContain('?_a=BAZAlhAMA');
})).toContain('?_a=DAZAlhAMZAAA');
});

it('Test placeholder feature value', () => {
Expand All @@ -91,7 +91,7 @@ describe('Add analytics to a regular URL', () => {
techVersion: '12.0.0',
placeholder: true
}
})).toContain('?_a=BAZAlhAMB');
})).toContain('?_a=DAZAlhAMZAAB');
});

it('Test product letter', () => {
Expand All @@ -103,7 +103,33 @@ describe('Add analytics to a regular URL', () => {
techVersion: '12.0.0',
product: 'B'
}
})).toContain('?_a=BBZAlhAM0');
})).toContain('?_a=DBZAlhAMZAA0');
});

it('Test OS type letter', () => {
const cldImage = createNewImageWithAnalytics('sample');
expect(cldImage.toURL({
trackedAnalytics: {
sdkCode: 'Z',
sdkSemver: '1.24.0',
techVersion: '12.0.0',
product: 'B',
osType: 'A'
}
})).toContain('?_a=DBZAlhAMAAA0');
});

it('Test OS version letters', () => {
const cldImage = createNewImageWithAnalytics('sample');
expect(cldImage.toURL({
trackedAnalytics: {
sdkCode: 'Z',
sdkSemver: '1.24.0',
techVersion: '12.0.0',
product: 'B',
osVersion: '16.3'
}
})).toContain('?_a=DBZAlhAMZQD0');
});

it('Can be turned off', () => {
Expand Down
4 changes: 2 additions & 2 deletions __TESTS__/unit/url/url.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ describe('Tests for URL configuration', () => {
sdkSemver: '1.0.0'
};
const url = image.toURL({trackedAnalytics: analyticsOptions});
expect(url).toEqual(`https://res.cloudinary.com/demo/image/upload/sample?_i=abcde&_a=BATAABAQ0`);
expect(url).toEqual(`https://res.cloudinary.com/demo/image/upload/sample?_i=abcde&_a=DATAABAQZAA0`);
});

it('Should include query params with analytics when passed as a string', function () {
Expand All @@ -126,7 +126,7 @@ describe('Tests for URL configuration', () => {
sdkSemver: '1.0.0'
};
const url = image.toURL({trackedAnalytics: analyticsOptions});
expect(url).toEqual(`https://res.cloudinary.com/demo/image/upload/sample?_i=abcde&_z=1234&_t=false&_a=BATAABAQ0`);
expect(url).toEqual(`https://res.cloudinary.com/demo/image/upload/sample?_i=abcde&_z=1234&_t=false&_a=DATAABAQZAA0`);
});

});
21 changes: 21 additions & 0 deletions src/sdkAnalytics/encodeOSVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {base64Map} from "./base64Map.js";

/**
* @private
* @description Encodes a semVer-like version string for OS
* @param {string} semVer Input is x.y
* @return {string} A string built from 2 characters of the base64 table that encode the semVer
*/
export function encodeOSVersion(semVer: string):string {
const [major, minor] = semVer.split('.');

//convert to binary
const binaryMajorVersion = parseInt(major).toString(2);
const binaryMinorVersion = parseInt(minor).toString(2);

//pad to 6
const paddedMajor = binaryMajorVersion.padStart(6, '0');
const paddedMinor = binaryMinorVersion.padStart(6, '0');

return base64Map[paddedMajor]+base64Map[paddedMinor];
}
11 changes: 4 additions & 7 deletions src/sdkAnalytics/encodeVersion.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {base64Map} from "./base64Map.js";
import {stringPad} from "./stringPad.js";
import {reverseVersion} from "./reverseVersion.js";
import {padVersion} from "./padVersion.js";

/**
* @private
Expand All @@ -14,20 +15,16 @@ export function encodeVersion(semVer: string):string {
// support x.y or x.y.z by using 'parts' as a variable
const parts = semVer.split('.').length;
const paddedStringLength = parts * 6; // we pad to either 12 or 18 characters

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

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

const num = parseInt(paddedSemver.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) {
Expand Down
2 changes: 2 additions & 0 deletions src/sdkAnalytics/getAnalyticsOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export function getAnalyticsOptions(options: ITrackedPropertiesThroughAnalytics)
sdkCode: options.sdkCode,
product: options.product,
feature: '0',
osType: options.osType,
osVersion: options.osVersion,
};

if (options.accessibility) {
Expand Down
10 changes: 7 additions & 3 deletions src/sdkAnalytics/getSDKAnalyticsSignature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {encodeVersion} from "./encodeVersion.js";
import {getAnalyticsOptions} from "./getAnalyticsOptions.js";
import {ITrackedPropertiesThroughAnalytics} from "./interfaces/ITrackedPropertiesThroughAnalytics.js";
import {packageVersion} from "../internal/utils/packageVersion.js";
import {encodeOSVersion} from "./encodeOSVersion.js";

/**
* @private
Expand Down Expand Up @@ -31,6 +32,8 @@ function ensureShapeOfTrackedProperties(trackedAnalytics?: Partial<ITrackedPrope
sdkCode: 'T', // Base code
sdkSemver : packageVersion.split('-')[0], // remove -beta, -alpha or other tagged versions from the version string
product: 'A',
osType: 'Z',
osVersion: '0.0',
responsive: false,
placeholder: false,
lazyload: false,
Expand Down Expand Up @@ -69,14 +72,15 @@ export function getSDKAnalyticsSignature(_trackedAnalytics?: Partial<ITrackedPro
const twoPartVersion = removePatchFromSemver(analyticsOptions.techVersion);
const encodedSDKVersion = encodeVersion(analyticsOptions.sdkSemver);
const encodedTechVersion = encodeVersion(twoPartVersion);
const encodedOSVersion = encodeOSVersion(analyticsOptions.osVersion);

const featureCode = analyticsOptions.feature;
const SDKCode = analyticsOptions.sdkCode;
const product = analyticsOptions.product;
const algoVersion = 'B'; // The algo version is determined here, it should not be an argument
const {product, osType } = analyticsOptions;
const algoVersion = 'D'; // The algo version is determined here, it should not be an argument


return `${algoVersion}${product}${SDKCode}${encodedSDKVersion}${encodedTechVersion}${featureCode}`;
return `${algoVersion}${product}${SDKCode}${encodedSDKVersion}${encodedTechVersion}${osType}${encodedOSVersion}${featureCode}`;
} catch (e) {
// Either SDK or Node versions were unparsable
return 'E';
Expand Down
2 changes: 2 additions & 0 deletions src/sdkAnalytics/interfaces/IAnalyticsOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ export interface IAnalyticsOptions {
sdkCode: string,
feature: string,
product: string,
osType: string,
osVersion: string,
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export interface ITrackedPropertiesThroughAnalytics {
techVersion: string; // Node Version or 1.0.0 by default
sdkCode: string; // Constant for Base?
product?: string; // Product code, 'A' for classic, 'B' for integrations
osType?: string; // OS code, 'A' for android, 'B' for iOS, defaults to 'Z'
osVersion?: string; // OS version,
accessibility?: boolean; // Was accessibility used
lazyload?: boolean; // Was lazy-load used
responsive?: boolean; // Was responsive used
Expand Down
25 changes: 25 additions & 0 deletions src/sdkAnalytics/padVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {stringPad} from "./stringPad.js";

/**
* @private
* @description Pads each segment with '0' so they have length of 2
* @param {string} semVer Input can be either x.y.z or x.y
* @return {string} in the form of xx.yy.zz (
*/
export function padVersion(semVer: string): string {
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('.').map((segment) => {
// try to cast to number
const asNumber = +segment;

if (isNaN(asNumber) || asNumber < 0) {
throw 'Invalid version number provided';
}

return stringPad(segment, 2, '0');
}).join('.');
}
16 changes: 2 additions & 14 deletions src/sdkAnalytics/reverseVersion.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import {stringPad} from "./stringPad.js";

/**
* @private
* @description 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 (
Expand All @@ -12,16 +10,6 @@ export function reverseVersion(semVer: string): string {
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((segment) => {
// try to cast to number
const asNumber = +segment;

if (isNaN(asNumber) || asNumber < 0) {
throw 'Invalid version number provided';
}

return stringPad(segment, 2, '0');
}).join('.');
// Split by '.', reverse, create new array
return semVer.split('.').reverse().join('.');
}