This is an automated email from the ASF dual-hosted git repository. jky pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/flagon-useralejs.git
The following commit(s) were added to refs/heads/master by this push: new 4eec2ff Moved attributes and CSS to main package 4eec2ff is described below commit 4eec2ffd87880bc96858f2802b054058eeebb003 Author: Jason Young <jason_...@protonmail.com> AuthorDate: Tue Aug 20 12:57:33 2024 -0700 Moved attributes and CSS to main package --- build/UserALEWebExtension/background.js | 43 +++++++++++++++ build/UserALEWebExtension/content.js | 43 +++++++++++++++ build/UserALEWebExtension/options.js | 43 +++++++++++++++ build/packageLogs.d.ts | 13 +++++ build/userale-2.4.0.js | 43 +++++++++++++++ build/userale-2.4.0.js.map | 2 +- build/userale-2.4.0.min.js | 2 +- example/log-attribute-example/README.md | 95 -------------------------------- example/log-attribute-example/index.html | 82 --------------------------- example/log.schema.json | 2 +- src/packageLogs.ts | 45 +++++++++++++++ test/e2e/spec/02-shared.spec.ts | 7 ++- test/unit/spec/packageLogs.spec.ts | 42 ++++++++++++++ 13 files changed, 279 insertions(+), 183 deletions(-) diff --git a/build/UserALEWebExtension/background.js b/build/UserALEWebExtension/background.js index 17746af..3b625d6 100644 --- a/build/UserALEWebExtension/background.js +++ b/build/UserALEWebExtension/background.js @@ -619,6 +619,8 @@ function packageLog(e, detailFcn) { sessionId: config$1.sessionId, httpSessionId: config$1.httpSessionId, browserSessionId: config$1.browserSessionId, + attributes: buildAttrs(e), + style: buildCSS(e), }; for (const func of Object.values(cbHandlers)) { if (typeof func === "function") { @@ -855,6 +857,47 @@ function detectBrowser() { version: browserInfo ? browserInfo.version : "", }; } +/** + * Builds an object containing attributes of an element. + * Attempts to parse all attribute values as JSON text. + * @param {Event} e Event from which the target element's attributes should be extracted. + * @return {Record<string, any>} Object with element attributes as key-value pairs. + */ +function buildAttrs(e) { + const attributes = {}; + const attributeBlackList = ["style"]; + if (e.target && e.target instanceof Element) { + for (const attr of e.target.attributes) { + if (attributeBlackList.includes(attr.name)) + continue; + let val = attr.value; + try { + val = JSON.parse(val); + } + catch (error) { + // Ignore parsing errors, fallback to raw string value + } + attributes[attr.name] = val; + } + } + return attributes; +} +/** + * Builds an object containing all CSS properties of an element. + * @param {Event} e Event from which the target element's properties should be extracted. + * @return {Record<string, string>} Object with all CSS properties as key-value pairs. + */ +function buildCSS(e) { + const properties = {}; + if (e.target && e.target instanceof HTMLElement) { + const styleObj = e.target.style; + for (let i = 0; i < styleObj.length; i++) { + const prop = styleObj[i]; + properties[prop] = styleObj.getPropertyValue(prop); + } + } + return properties; +} /* * Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/build/UserALEWebExtension/content.js b/build/UserALEWebExtension/content.js index 1709798..9696353 100644 --- a/build/UserALEWebExtension/content.js +++ b/build/UserALEWebExtension/content.js @@ -566,6 +566,8 @@ function packageLog(e, detailFcn) { sessionId: config$1.sessionId, httpSessionId: config$1.httpSessionId, browserSessionId: config$1.browserSessionId, + attributes: buildAttrs(e), + style: buildCSS(e), }; for (const func of Object.values(cbHandlers)) { if (typeof func === "function") { @@ -802,6 +804,47 @@ function detectBrowser() { version: browserInfo ? browserInfo.version : "", }; } +/** + * Builds an object containing attributes of an element. + * Attempts to parse all attribute values as JSON text. + * @param {Event} e Event from which the target element's attributes should be extracted. + * @return {Record<string, any>} Object with element attributes as key-value pairs. + */ +function buildAttrs(e) { + const attributes = {}; + const attributeBlackList = ["style"]; + if (e.target && e.target instanceof Element) { + for (const attr of e.target.attributes) { + if (attributeBlackList.includes(attr.name)) + continue; + let val = attr.value; + try { + val = JSON.parse(val); + } + catch (error) { + // Ignore parsing errors, fallback to raw string value + } + attributes[attr.name] = val; + } + } + return attributes; +} +/** + * Builds an object containing all CSS properties of an element. + * @param {Event} e Event from which the target element's properties should be extracted. + * @return {Record<string, string>} Object with all CSS properties as key-value pairs. + */ +function buildCSS(e) { + const properties = {}; + if (e.target && e.target instanceof HTMLElement) { + const styleObj = e.target.style; + for (let i = 0; i < styleObj.length; i++) { + const prop = styleObj[i]; + properties[prop] = styleObj.getPropertyValue(prop); + } + } + return properties; +} /* * Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/build/UserALEWebExtension/options.js b/build/UserALEWebExtension/options.js index fc8ef2c..91d7f82 100644 --- a/build/UserALEWebExtension/options.js +++ b/build/UserALEWebExtension/options.js @@ -566,6 +566,8 @@ function packageLog(e, detailFcn) { sessionId: config$1.sessionId, httpSessionId: config$1.httpSessionId, browserSessionId: config$1.browserSessionId, + attributes: buildAttrs(e), + style: buildCSS(e), }; for (const func of Object.values(cbHandlers)) { if (typeof func === "function") { @@ -802,6 +804,47 @@ function detectBrowser() { version: browserInfo ? browserInfo.version : "", }; } +/** + * Builds an object containing attributes of an element. + * Attempts to parse all attribute values as JSON text. + * @param {Event} e Event from which the target element's attributes should be extracted. + * @return {Record<string, any>} Object with element attributes as key-value pairs. + */ +function buildAttrs(e) { + const attributes = {}; + const attributeBlackList = ["style"]; + if (e.target && e.target instanceof Element) { + for (const attr of e.target.attributes) { + if (attributeBlackList.includes(attr.name)) + continue; + let val = attr.value; + try { + val = JSON.parse(val); + } + catch (error) { + // Ignore parsing errors, fallback to raw string value + } + attributes[attr.name] = val; + } + } + return attributes; +} +/** + * Builds an object containing all CSS properties of an element. + * @param {Event} e Event from which the target element's properties should be extracted. + * @return {Record<string, string>} Object with all CSS properties as key-value pairs. + */ +function buildCSS(e) { + const properties = {}; + if (e.target && e.target instanceof HTMLElement) { + const styleObj = e.target.style; + for (let i = 0; i < styleObj.length; i++) { + const prop = styleObj[i]; + properties[prop] = styleObj.getPropertyValue(prop); + } + } + return properties; +} /* * Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/build/packageLogs.d.ts b/build/packageLogs.d.ts index a34e759..b94fe8f 100644 --- a/build/packageLogs.d.ts +++ b/build/packageLogs.d.ts @@ -113,3 +113,16 @@ export declare function detectBrowser(): { browser: string; version: string | null; }; +/** + * Builds an object containing attributes of an element. + * Attempts to parse all attribute values as JSON text. + * @param {Event} e Event from which the target element's attributes should be extracted. + * @return {Record<string, any>} Object with element attributes as key-value pairs. + */ +export declare function buildAttrs(e: Event): Record<string, any>; +/** + * Builds an object containing all CSS properties of an element. + * @param {Event} e Event from which the target element's properties should be extracted. + * @return {Record<string, string>} Object with all CSS properties as key-value pairs. + */ +export declare function buildCSS(e: Event): Record<string, string>; diff --git a/build/userale-2.4.0.js b/build/userale-2.4.0.js index ec004d6..8832057 100644 --- a/build/userale-2.4.0.js +++ b/build/userale-2.4.0.js @@ -577,6 +577,8 @@ sessionId: config$1.sessionId, httpSessionId: config$1.httpSessionId, browserSessionId: config$1.browserSessionId, + attributes: buildAttrs(e), + style: buildCSS(e), }; for (const func of Object.values(cbHandlers)) { if (typeof func === "function") { @@ -813,6 +815,47 @@ version: browserInfo ? browserInfo.version : "", }; } + /** + * Builds an object containing attributes of an element. + * Attempts to parse all attribute values as JSON text. + * @param {Event} e Event from which the target element's attributes should be extracted. + * @return {Record<string, any>} Object with element attributes as key-value pairs. + */ + function buildAttrs(e) { + const attributes = {}; + const attributeBlackList = ["style"]; + if (e.target && e.target instanceof Element) { + for (const attr of e.target.attributes) { + if (attributeBlackList.includes(attr.name)) + continue; + let val = attr.value; + try { + val = JSON.parse(val); + } + catch (error) { + // Ignore parsing errors, fallback to raw string value + } + attributes[attr.name] = val; + } + } + return attributes; + } + /** + * Builds an object containing all CSS properties of an element. + * @param {Event} e Event from which the target element's properties should be extracted. + * @return {Record<string, string>} Object with all CSS properties as key-value pairs. + */ + function buildCSS(e) { + const properties = {}; + if (e.target && e.target instanceof HTMLElement) { + const styleObj = e.target.style; + for (let i = 0; i < styleObj.length; i++) { + const prop = styleObj[i]; + properties[prop] = styleObj.getPropertyValue(prop); + } + } + return properties; + } /* * Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/build/userale-2.4.0.js.map b/build/userale-2.4.0.js.map index af6f712..206c070 100644 --- a/build/userale-2.4.0.js.map +++ b/build/userale-2.4.0.js.map @@ -1 +1 @@ -{"version":3,"file":"userale-2.4.0.js","sources":["../src/getInitialSettings.ts","../src/configure.ts","../node_modules/detect-browser/es/index.js","../src/packageLogs.ts","../src/attachHandlers.ts","../src/utils/auth/index.ts","../src/sendLogs.ts","../src/main.ts"],"sourcesContent":[null,null,"var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i i [...] \ No newline at end of file +{"version":3,"file":"userale-2.4.0.js","sources":["../src/getInitialSettings.ts","../src/configure.ts","../node_modules/detect-browser/es/index.js","../src/packageLogs.ts","../src/attachHandlers.ts","../src/utils/auth/index.ts","../src/sendLogs.ts","../src/main.ts"],"sourcesContent":[null,null,"var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i i [...] \ No newline at end of file diff --git a/build/userale-2.4.0.min.js b/build/userale-2.4.0.min.js index 233c021..a5315f7 100644 --- a/build/userale-2.4.0.min.js +++ b/build/userale-2.4.0.min.js @@ -15,4 +15,4 @@ * limitations under the License. * @preserved */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).userale={})}(this,(function(e){"use strict";var t="2.4.0";let n=null,o=null;function r(){null===n&&(n=i("userAlesessionId","session_"+String(Date.now()))),null===o&&(o=i("userAleHttpSessionId",function(){const e=new Uint8Array(16);return window.crypto.getRandomValues(e),Array.from(e,(e=>e.toStrin [...] +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).userale={})}(this,(function(e){"use strict";var t="2.4.0";let n=null,o=null;function r(){null===n&&(n=i("userAlesessionId","session_"+String(Date.now()))),null===o&&(o=i("userAleHttpSessionId",function(){const e=new Uint8Array(16);return window.crypto.getRandomValues(e),Array.from(e,(e=>e.toStrin [...] diff --git a/example/log-attribute-example/README.md b/example/log-attribute-example/README.md deleted file mode 100644 index 8699d16..0000000 --- a/example/log-attribute-example/README.md +++ /dev/null @@ -1,95 +0,0 @@ -<!-- - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> -# HTML attribute logging - -The following is an example outlining how to log HTML attributes with UserALE, and parse some common syntaxs found in attributes. - -## Common syntaxes - -Consider the following HTML canvas element. This element has a attribute, `data-name`, containing JSON data, `{"name" : "John Doe"}`, with special characters encoded for HTML. And another attribute, `style`, containing CSS formatted data. - -```html -<div> - <canvas id="myCanvas" data-name="{"name": "John Doe"}" width="200" height="100" style="border:1px solid #000000;"></canvas> -</div> -``` - -## Extracting and parsing attributes - -The following function shows how to extract attributes and parse JSON attribute values. The `style` attribute is excluded because it is handled as a special case in the next code block. - -```js - -/** - * Builds an object containing attributes of an element. - * Attempts to parse all attribute values as JSON text. - * @param {Object} e Event from which the target elemnet's attributes should be extracted from. - * @return {Object} Object with element attributes as key value pairs. - */ -function buildAttrs(e) { - let attributes = {}; - let attributeBlackList = ["style"]; - if(e.target && e.target instanceof Element) { - for(attr of e.target.attributes) { - if(attributeBlackList.includes(attr.name)) - continue; - let val = attr.value; - try { - val = JSON.parse(val); - } catch (error) {} - attributes[attr.name] = val; - } - } - return attributes; -} -``` - -The following function will parse the CSS found in the style attribute. - -```js -/** - * Builds an object containing all css properties of an element. - * @param {Object} e Event from which the target elemnet's properties should be extracted from. - * @return {Object} Object with all CSS properties as key value pairs. - */ -export function buildCSS(e) { - let properties = {}; - if(e.target && e.target instanceof Element) { - let styleObj = e.target.style - for(prop of styleObj) { - properties[prop] = styleObj.getPropertyValue(prop); - } - } - return properties; -} -``` -The above functions can be used in the below snippet to add the results to each log message. In turn, this snippet can be added to a custom.js script to modify core UserALE behavior. See the 'index.js' example in this dir. - -```js -window.userale.addCallbacks({ - map(log, e) { - return { - ...log, - attributes: buildAttrs(e), - style : buildCSS(e), - logType: 'custom', - }; - } -}); -``` \ No newline at end of file diff --git a/example/log-attribute-example/index.html b/example/log-attribute-example/index.html deleted file mode 100644 index 2c3da4f..0000000 --- a/example/log-attribute-example/index.html +++ /dev/null @@ -1,82 +0,0 @@ -<!-- -Licensed to the Apache Software Foundation (ASF) under one or more -contributor license agreements. See the NOTICE file distributed with -this work for additional information regarding copyright ownership. -The ASF licenses this file to You under the Apache License, Version 2.0 -(the "License"); you may not use this file except in compliance with -the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. ---> -<html> -<head> - <script - src="../../build/userale-2.4.0.min.js" - data-url="" - data-tool="Apache UserALE Example" - data-threshold="1" - data-interval="100" - ></script> -</head> -<body> -<div class="new-feature-button"> - <canvas id="myCanvas" data-name="{"name": "John Doe"}" width="200" height="100" style="border:1px solid #000000;"> - </canvas> -</div> -</body> -<script> - /** - * Builds an object containing attributes of an element. - * Attempts to parse all attribute values as JSON text. - * @param {Object} e Event from which the target elemnet's attributes should be extracted from. - * @return {Object} Object with element attributes as key value pairs. - */ - function buildAttrs(e) { - let attributes = {}; - let attributeBlackList = ["style"]; - if(e.target && e.target instanceof Element) { - for(attr of e.target.attributes) { - if(attributeBlackList.includes(attr.name)) - continue; - let val = attr.value; - try { - val = JSON.parse(val); - } catch (error) {} - attributes[attr.name] = val; - } - } - return attributes; - } - - /** - * Builds an object containing all css properties of an element. - * @param {Object} e Event from which the target elemnet's properties should be extracted from. - * @return {Object} Object with all CSS properties as key value pairs. - */ - export function buildCSS(e) { - let properties = {}; - if(e.target && e.target instanceof Element) { - let styleObj = e.target.style - for(prop of styleObj) { - properties[prop] = styleObj.getPropertyValue(prop); - } - } - return properties; - } - - window.userale.addCallbacks({ - map(log, e) { - return { - ...log, - attributes: buildAttrs(e), - style : buildCSS(e), - logType: 'custom', - }; - } - }); -</script> -</html> \ No newline at end of file diff --git a/example/log.schema.json b/example/log.schema.json index e98d282..83434b5 100644 --- a/example/log.schema.json +++ b/example/log.schema.json @@ -76,7 +76,7 @@ "type": "boolean" }, "details": { - "type": "object" + "type": ["object", "null"] }, "userId": { "type": "string" diff --git a/src/packageLogs.ts b/src/packageLogs.ts index a3878d6..2cb0cbe 100644 --- a/src/packageLogs.ts +++ b/src/packageLogs.ts @@ -138,6 +138,8 @@ export function packageLog( sessionId: config.sessionId, httpSessionId: config.httpSessionId, browserSessionId: config.browserSessionId, + attributes: buildAttrs(e), + style: buildCSS(e), }; if (typeof filterHandler === "function" && !filterHandler(log)) { @@ -424,3 +426,46 @@ export function detectBrowser() { version: browserInfo ? browserInfo.version : "", }; } + +/** + * Builds an object containing attributes of an element. + * Attempts to parse all attribute values as JSON text. + * @param {Event} e Event from which the target element's attributes should be extracted. + * @return {Record<string, any>} Object with element attributes as key-value pairs. + */ +export function buildAttrs(e: Event): Record<string, any> { + const attributes: Record<string, any> = {}; + const attributeBlackList = ["style"]; + + if (e.target && e.target instanceof Element) { + for (const attr of e.target.attributes) { + if (attributeBlackList.includes(attr.name)) continue; + let val: any = attr.value; + try { + val = JSON.parse(val); + } catch (error) { + // Ignore parsing errors, fallback to raw string value + } + attributes[attr.name] = val; + } + } + + return attributes; +} + +/** + * Builds an object containing all CSS properties of an element. + * @param {Event} e Event from which the target element's properties should be extracted. + * @return {Record<string, string>} Object with all CSS properties as key-value pairs. + */ +export function buildCSS(e: Event): Record<string, string> { + const properties: Record<string, string> = {}; + if (e.target && e.target instanceof HTMLElement) { + const styleObj = e.target.style; + for (let i = 0; i < styleObj.length; i++) { + const prop = styleObj[i]; + properties[prop] = styleObj.getPropertyValue(prop); + } + } + return properties; +} diff --git a/test/e2e/spec/02-shared.spec.ts b/test/e2e/spec/02-shared.spec.ts index ebaa4d5..eb65102 100644 --- a/test/e2e/spec/02-shared.spec.ts +++ b/test/e2e/spec/02-shared.spec.ts @@ -82,12 +82,13 @@ test.describe("Userale logging", () => { } const request = await requestPromise; const body = await request.postDataJSON(); - console.log(body); var schema = require("../../../example/log.schema.json"); for (const log of body) { - const result = validate(log, schema); - expect(result.valid).toBe(true); + if (log.logType == "raw" || log.logType == "custom") { + const result = validate(log, schema); + expect(result.valid).toBe(true); + } } }); }); diff --git a/test/unit/spec/packageLogs.spec.ts b/test/unit/spec/packageLogs.spec.ts index f827ab4..3de5fc0 100644 --- a/test/unit/spec/packageLogs.spec.ts +++ b/test/unit/spec/packageLogs.spec.ts @@ -29,6 +29,8 @@ import { packageLog, removeCallbacks, selectorizePath, + buildAttrs, + buildCSS, } from "@/packageLogs"; import type { Logging } from "@/types"; @@ -381,4 +383,44 @@ describe("packageLogs", () => { expect(actualPath).toEqual(expectedPath); }); }); + + test("buildAttrs", () => { + let result; + const document = window.document; + const ele = document.createElement("div"); + const evt = new window.Event("CustomEvent", { + bubbles: true, + cancelable: true, + }); + ele.setAttribute("data-json", '{"key": "value"}'); + ele.setAttribute("data-string", "hello"); + ele.setAttribute("style", "color: red;"); + document.body.appendChild(ele); + ele.addEventListener("CustomEvent", (e) => (result = buildAttrs(e))); + ele.dispatchEvent(evt); + expect(result).toEqual({ + "data-json": { key: "value" }, + "data-string": "hello", + }); + expect(result).not.toHaveProperty("style"); + }); + + test("buildCSS", () => { + let result; + const document = window.document; + const ele = document.createElement("div"); + const evt = new window.Event("CustomEvent", { + bubbles: true, + cancelable: true, + }); + ele.style.color = "red"; + ele.style.marginTop = "10px"; + document.body.appendChild(ele); + ele.addEventListener("CustomEvent", (e) => (result = buildCSS(e))); + ele.dispatchEvent(evt); + expect(result).toEqual({ + color: "red", + "margin-top": "10px", + }); + }); });