diff --git a/__TESTS_BUNDLE_SIZE__/bundleSizeTestCases.ts b/__TESTS_BUNDLE_SIZE__/bundleSizeTestCases.ts index 1ced13ce..9c6ba8b4 100644 --- a/__TESTS_BUNDLE_SIZE__/bundleSizeTestCases.ts +++ b/__TESTS_BUNDLE_SIZE__/bundleSizeTestCases.ts @@ -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'), @@ -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'), @@ -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'), @@ -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'), @@ -56,7 +56,7 @@ const bundleSizeTestCases:ITestCase[] = [ }, { name: 'Tests Overlay imports', - sizeLimitInKB: 30, + sizeLimitInKB: 31, importsArray: [ importFromDist('assets/CloudinaryImage', 'CloudinaryImage'), importFromDist('actions/overlay', 'Overlay'), diff --git a/__TESTS__/unit/analytics/analytics.browser.test.ts b/__TESTS__/unit/analytics/analytics.browser.test.ts index 34610e48..c5216098 100644 --- a/__TESTS__/unit/analytics/analytics.browser.test.ts +++ b/__TESTS__/unit/analytics/analytics.browser.test.ts @@ -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', () => { @@ -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', () => { @@ -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 }); @@ -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 }); }); diff --git a/__TESTS__/unit/analytics/analytics.node.test.ts b/__TESTS__/unit/analytics/analytics.node.test.ts index 5aedf02a..9fc73667 100644 --- a/__TESTS__/unit/analytics/analytics.node.test.ts +++ b/__TESTS__/unit/analytics/analytics.node.test.ts @@ -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', () => { @@ -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', () => { @@ -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', () => { @@ -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', () => { @@ -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', () => { diff --git a/__TESTS__/unit/url/url.test.ts b/__TESTS__/unit/url/url.test.ts index dcfe9d43..506b05ca 100644 --- a/__TESTS__/unit/url/url.test.ts +++ b/__TESTS__/unit/url/url.test.ts @@ -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 () { @@ -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`); }); }); diff --git a/src/sdkAnalytics/encodeOSVersion.ts b/src/sdkAnalytics/encodeOSVersion.ts new file mode 100644 index 00000000..7f1dcc9c --- /dev/null +++ b/src/sdkAnalytics/encodeOSVersion.ts @@ -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]; +} diff --git a/src/sdkAnalytics/encodeVersion.ts b/src/sdkAnalytics/encodeVersion.ts index c6405796..54b12ff7 100644 --- a/src/sdkAnalytics/encodeVersion.ts +++ b/src/sdkAnalytics/encodeVersion.ts @@ -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 @@ -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) { diff --git a/src/sdkAnalytics/getAnalyticsOptions.ts b/src/sdkAnalytics/getAnalyticsOptions.ts index 6b472923..43e9cd8c 100644 --- a/src/sdkAnalytics/getAnalyticsOptions.ts +++ b/src/sdkAnalytics/getAnalyticsOptions.ts @@ -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) { diff --git a/src/sdkAnalytics/getSDKAnalyticsSignature.ts b/src/sdkAnalytics/getSDKAnalyticsSignature.ts index 5fc3e1da..2c1c770f 100644 --- a/src/sdkAnalytics/getSDKAnalyticsSignature.ts +++ b/src/sdkAnalytics/getSDKAnalyticsSignature.ts @@ -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 @@ -31,6 +32,8 @@ function ensureShapeOfTrackedProperties(trackedAnalytics?: Partial { + // try to cast to number + const asNumber = +segment; + + if (isNaN(asNumber) || asNumber < 0) { + throw 'Invalid version number provided'; + } + + return stringPad(segment, 2, '0'); + }).join('.'); +} diff --git a/src/sdkAnalytics/reverseVersion.ts b/src/sdkAnalytics/reverseVersion.ts index b6eb42c8..ef3ba677 100644 --- a/src/sdkAnalytics/reverseVersion.ts +++ b/src/sdkAnalytics/reverseVersion.ts @@ -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 ( @@ -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('.'); }