diff --git a/.forceignore b/.forceignore index 7b5b5a7..3d36001 100755 --- a/.forceignore +++ b/.forceignore @@ -9,4 +9,7 @@ package.xml **/.eslintrc.json # LWC Jest -**/__tests__/** \ No newline at end of file +**/__tests__/** +**/tsconfig.json + +**/*.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e7c268d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "salesforcedx-vscode-core.show-cli-success-msg": false +} \ No newline at end of file diff --git a/force-app/main/default/classes/TritonDmlDemoController.cls b/force-app/main/default/classes/TritonDmlDemoController.cls new file mode 100644 index 0000000..79cdffb --- /dev/null +++ b/force-app/main/default/classes/TritonDmlDemoController.cls @@ -0,0 +1,12 @@ +/** + @description LWC Apex Api Class for use to demo Triton + */ +public with sharing class TritonDmlDemoController { + + public class TritonDmlDemoControllerException extends Exception {} + + @AuraEnabled + public static String triggerDmlException() { + throw new TritonDmlDemoControllerException('Error Msg'); + } +} diff --git a/force-app/main/default/classes/TritonDmlDemoController.cls-meta.xml b/force-app/main/default/classes/TritonDmlDemoController.cls-meta.xml new file mode 100644 index 0000000..7d5f9e8 --- /dev/null +++ b/force-app/main/default/classes/TritonDmlDemoController.cls-meta.xml @@ -0,0 +1,5 @@ + + + 61.0 + Active + \ No newline at end of file diff --git a/force-app/main/default/classes/TritonLwc.cls b/force-app/main/default/classes/TritonLwc.cls index 3f11c6d..ecc5437 100644 --- a/force-app/main/default/classes/TritonLwc.cls +++ b/force-app/main/default/classes/TritonLwc.cls @@ -18,39 +18,44 @@ public with sharing class TritonLwc { /** * Create component logs from LWC * Use this method to persist logs generated from LWC components - * @param componentLogs -- a collection of ComponentLog objects + * @param componentLogs -- a collection of ComponentLog objects serialized */ @AuraEnabled - public static void saveComponentLogs(List componentLogs) { + public static void saveComponentLogs(String pComponentLogs) { + System.debug('pComponentLogs: ' + pComponentLogs); + List componentLogs = (List)JSON.deserialize( + pComponentLogs, + List.class + ); Triton logInstance = Triton.instance; - for (ComponentLog componentLog : componentLogs) { + for (TritonLwc.ComponentLog componentLog : componentLogs) { logInstance.add( - Triton.makeBuilder() - //category will be fetched from the componentLog - .category(String.isNotBlank(componentLog.category) ? componentLog.category : componentLog.component.category) - //type will be fetched from the componentLog directly, of from the error. If neither are set, Frontend will be used - .type(String.isNotBlank(componentLog.type) ? componentLog.type : - componentLog.error != null ? componentLog.error.type : TritonTypes.Type.Frontend.name()) - //area will be fetched from the componentLog directly if set. Otherwise component name will be used - .area(String.isNotBlank(componentLog.area) ? componentLog.area : componentLog.component.name) - //summary will be fetched from the componentLog directly if set. Otherwise, error message will be used if provided. - .summary(String.isNotBlank(componentLog.summary) ? componentLog.summary : - componentLog.error != null ? componentLog.error.message : null) - .stackTrace(componentLog.stack) - .details(componentLog.details) - //transaction id will be used from the componentLog, or a new transaction id will be generated - .transactionId(String.isNotBlank(componentLog.transactionId) ? componentLog.transactionId : logInstance.TRANSACTION_ID) - .attribute(Triton.USER_ID, componentLog.userId) - //apex name will be set to component.function or component.action - .attribute(Triton.APEX_NAME, componentLog.component.name + '.' + - (String.isNotBlank(componentLog.component.function) ? componentLog.component.function : componentLog.component.action)) - .attribute(Triton.RELATED_ID, componentLog.recordId) - //created timestamp will be either set from the componentLog if provided, otherwise current timestamp will be used - .attribute(Triton.CREATED_TIMESTAMP, componentLog.createdTimestamp != null ? Double.valueOf(componentLog.createdTimestamp) : Double.valueOf(System.now().getTime())) - //log level will be taken from the componentLog if provided, otherwise INFO will be used - .attribute(Triton.LOG_LEVEL, String.isNotBlank(componentLog.level) ? componentLog.level : TritonTypes.Level.INFO.name()) - .attribute(Triton.DURATION, componentLog.duration) - .build()); + Triton.makeBuilder() + //category will be fetched from the componentLog + .category(String.isNotBlank(componentLog.category) ? componentLog.category : componentLog.component.category) + //type will be fetched from the componentLog directly, of from the error. If neither are set, Frontend will be used + .type(String.isNotBlank(componentLog.type) ? componentLog.type : + componentLog.error != null ? componentLog.error.type : TritonTypes.Type.Frontend.name()) + //area will be fetched from the componentLog directly if set. Otherwise component name will be used + .area(String.isNotBlank(componentLog.area) ? componentLog.area : componentLog.component.name) + //summary will be fetched from the componentLog directly if set. Otherwise, error message will be used if provided. + .summary(String.isNotBlank(componentLog.summary) ? componentLog.summary : + componentLog.error != null ? componentLog.error.message : null) + .stackTrace(componentLog.stack) + .details(componentLog.details) + //transaction id will be used from the componentLog, or a new transaction id will be generated + .transactionId(String.isNotBlank(componentLog.transactionId) ? componentLog.transactionId : logInstance.TRANSACTION_ID) + .attribute(Triton.USER_ID, componentLog.userId) + //apex name will be set to component.function or component.action + .attribute(Triton.APEX_NAME, componentLog.component.name + '.' + + (String.isNotBlank(componentLog.component.function) ? componentLog.component.function : componentLog.component.action)) + .attribute(Triton.RELATED_ID, componentLog.recordId) + //created timestamp will be either set from the componentLog if provided, otherwise current timestamp will be used + .attribute(Triton.CREATED_TIMESTAMP, componentLog.createdTimestamp != null ? Double.valueOf(componentLog.createdTimestamp) : Double.valueOf(System.now().getTime())) + //log level will be taken from the componentLog if provided, otherwise INFO will be used + .attribute(Triton.LOG_LEVEL, String.isNotBlank(componentLog.level) ? componentLog.level : TritonTypes.Level.INFO.name()) + .attribute(Triton.DURATION, componentLog.duration) + .build()); } logInstance.flush(); } diff --git a/force-app/main/default/classes/TritonTest.cls b/force-app/main/default/classes/TritonTest.cls index a94bb62..6977727 100644 --- a/force-app/main/default/classes/TritonTest.cls +++ b/force-app/main/default/classes/TritonTest.cls @@ -247,7 +247,7 @@ private class TritonTest { component.function = 'test function'; componentLog.component = component; componentLogs.add(componentLog); - TritonLwc.saveComponentLogs(componentLogs); + TritonLwc.saveComponentLogs(JSON.serialize(componentLogs)); Test.stopTest(); List logs = [SELECT Id, pharos__Summary__c, pharos__Hash__c FROM pharos__Log__c]; diff --git a/force-app/main/default/lwc/triton/triton.js b/force-app/main/default/lwc/triton/triton.js index f30f6d9..087cdd7 100644 --- a/force-app/main/default/lwc/triton/triton.js +++ b/force-app/main/default/lwc/triton/triton.js @@ -10,164 +10,160 @@ * */ -import {LightningElement} from 'lwc'; -import {makeBuilder} from 'c/tritonBuilder'; +import { makeBuilder } from 'c/tritonBuilder'; import saveComponentLogs from '@salesforce/apex/TritonLwc.saveComponentLogs'; -export const Triton = LightningElement => - class extends LightningElement { - - /** - * Logs buffer - */ - logs = []; - - /** - * Add Log with LWC / Aura Category. - * This method will automatically get the stacktrace from Exception. - * Type will be obtained from Exception. If blank, a default Frontend Type will be saved - * Summary is the Exception message. - * Details will be a combination of Exception String and stacktrace - */ - addException(error) { - return this._makeBuilder().setError(error).setLevel(LEVEL.ERROR); - } - - /** - * Add Log with LWC / Aura Category. - */ - addError() { - return this._makeBuilder().setLevel(LEVEL.ERROR); - } - - /** - * Add Log with Warning Category. - */ - addWarning() { - return this._makeBuilder().setCategory(CATEGORY.WARNING).setLevel(LEVEL.WARNING); - } - - /** - * Add Log with Debug Category. - */ - addDebug() { - return this._makeBuilder().setCategory(CATEGORY.DEBUG).setLevel(LEVEL.DEBUG); - } - - /** - * Add Log with Event Category. - */ - addInfo() { - return this._makeBuilder().setCategory(CATEGORY.EVENT).setLevel(LEVEL.INFO); - } - - /** - * Save Log with LWC / Aura Category. - * This method will automatically get the stacktrace from Exception. - * Type will be obtained from Exception. If blank, a default Frontend Type will be saved - * Summary is the Exception message. - * Details will be a combination of Exception String and stacktrace - */ - exception(error, transactionId) { - this._makeBuilder() - .setError(error) - .setLevel(LEVEL.ERROR) - .setTransactionId(transactionId); - this.flush(); - } - - /** - * Save Log with LWC / Aura Category. - */ - error(type, area, summary, details, transactionId, component, duration, startTime) { - this._makeBuilder() - .setLevel(LEVEL.ERROR) - .setType(type) - .setArea(area) - .setSummary(summary) - .setDetails(details) - .setTransactionId(transactionId) - .setComponent(component) - .setDuration(duration) - .setCreatedTimestamp(startTime); - this.flush(); - } - - /** - * Save Log with Warning Category. - */ - warning(type, area, summary, details, transactionId, component, duration, startTime) { - this._makeBuilder() - .setLevel(LEVEL.WARNING) - .setCategory(CATEGORY.WARNING) - .setType(type) - .setArea(area) - .setSummary(summary) - .setDetails(details) - .setTransactionId(transactionId) - .setComponent(component) - .setDuration(duration) - .setCreatedTimestamp(startTime); - this.flush(); - } - - /** - * Save Log with Debug Category. - */ - debug(type, area, summary, details, transactionId, component, duration, startTime) { - this._makeBuilder() - .setLevel(LEVEL.DEBUG) - .setCategory(CATEGORY.DEBUG) - .setType(type) - .setArea(area) - .setSummary(summary) - .setDetails(details) - .setTransactionId(transactionId) - .setComponent(component) - .setDuration(duration) - .setCreatedTimestamp(startTime); - this.flush(); - } - - /** - * Save Log with Event Category. - */ - info(type, area, summary, details, level, transactionId, component, duration, startTime) { - this._makeBuilder() - .setLevel(level) - .setCategory(CATEGORY.EVENT) - .setType(type) - .setArea(area) - .setSummary(summary) - .setDetails(details) - .setTransactionId(transactionId) - .setComponent(component) - .setDuration(duration) - .setCreatedTimestamp(startTime); - this.flush(); - } - - /** - * Commit all logs previously added using the addXXX() methods. - */ - flush() { - saveComponentLogs({ - componentLogs: this.logs - }).then((data) => { - }).catch(error => { - console.error(error); - }); - this.logs = []; - } - - _makeBuilder() { - let logBuilder = makeBuilder(); - this.logs.push(logBuilder); - return logBuilder; - } +const Triton = class { + /** + * Logs buffer + */ + logs = []; + + /** + * Add Log with LWC / Aura Category. + * This method will automatically get the stacktrace from Exception. + * Type will be obtained from Exception. If blank, a default Frontend Type will be saved + * Summary is the Exception message. + * Details will be a combination of Exception String and stacktrace + */ + addException(error) { + return this._makeBuilder().setError(error).setLevel(LEVEL.ERROR); + } + + /** + * Add Log with LWC / Aura Category. + */ + addError() { + return this._makeBuilder().setLevel(LEVEL.ERROR); + } + + /** + * Add Log with Warning Category. + */ + addWarning() { + return this._makeBuilder().setCategory(CATEGORY.WARNING).setLevel(LEVEL.WARNING); + } + + /** + * Add Log with Debug Category. + */ + addDebug() { + return this._makeBuilder().setCategory(CATEGORY.DEBUG).setLevel(LEVEL.DEBUG); + } + + /** + * Add Log with Event Category. + */ + addInfo() { + return this._makeBuilder().setCategory(CATEGORY.EVENT).setLevel(LEVEL.INFO); + } + /** + * Save Log with LWC / Aura Category. + * This method will automatically get the stacktrace from Exception. + * Type will be obtained from Exception. If blank, a default Frontend Type will be saved + * Summary is the Exception message. + * Details will be a combination of Exception String and stacktrace + */ + exception(error, transactionId) { + this._makeBuilder() + .setError(error) + .setLevel(LEVEL.ERROR) + .setTransactionId(transactionId); + this.flush(); } + /** + * Save Log with LWC / Aura Category. + */ + error(type, area, summary, details, transactionId, component, duration, startTime) { + this._makeBuilder() + .setLevel(LEVEL.ERROR) + .setType(type) + .setArea(area) + .setSummary(summary) + .setDetails(details) + .setTransactionId(transactionId) + .setComponent(component) + .setDuration(duration) + .setCreatedTimestamp(startTime); + this.flush(); + } + + /** + * Save Log with Warning Category. + */ + warning(type, area, summary, details, transactionId, component, duration, startTime) { + this._makeBuilder() + .setLevel(LEVEL.WARNING) + .setCategory(CATEGORY.WARNING) + .setType(type) + .setArea(area) + .setSummary(summary) + .setDetails(details) + .setTransactionId(transactionId) + .setComponent(component) + .setDuration(duration) + .setCreatedTimestamp(startTime); + this.flush(); + } + + /** + * Save Log with Debug Category. + */ + debug(type, area, summary, details, transactionId, component, duration, startTime) { + this._makeBuilder() + .setLevel(LEVEL.DEBUG) + .setCategory(CATEGORY.DEBUG) + .setType(type) + .setArea(area) + .setSummary(summary) + .setDetails(details) + .setTransactionId(transactionId) + .setComponent(component) + .setDuration(duration) + .setCreatedTimestamp(startTime); + this.flush(); + } + + /** + * Save Log with Event Category. + */ + info(type, area, summary, details, level, transactionId, component, duration, startTime) { + this._makeBuilder() + .setLevel(level) + .setCategory(CATEGORY.EVENT) + .setType(type) + .setArea(area) + .setSummary(summary) + .setDetails(details) + .setTransactionId(transactionId) + .setComponent(component) + .setDuration(duration) + .setCreatedTimestamp(startTime); + this.flush(); + } + + /** + * Commit all logs previously added using the addXXX() methods. + */ + flush() { + saveComponentLogs({ + pComponentLogs: JSON.stringify(this.logs) + }).then((data) => { + }).catch(error => { + console.error(error); + }); + this.logs = []; + } + _makeBuilder() { + let logBuilder = makeBuilder(); + this.logs.push(logBuilder); + return logBuilder; + } + +}; + /** AREA */ export const AREA = { ACCOUNTS: 'ACCOUNTS', @@ -202,3 +198,5 @@ export const TYPE = { BACKEND: 'Backend', FRONTEND: 'Frontend' }; + +export default Triton; diff --git a/force-app/main/default/lwc/tritonDemo/__tests__/tritonDemo.test.js b/force-app/main/default/lwc/tritonDemo/__tests__/tritonDemo.test.js new file mode 100644 index 0000000..52bcbf7 --- /dev/null +++ b/force-app/main/default/lwc/tritonDemo/__tests__/tritonDemo.test.js @@ -0,0 +1,25 @@ +import { createElement } from 'lwc'; +import TritonDemo from 'c/tritonDemo'; + +describe('c-triton-demo', () => { + afterEach(() => { + // The jsdom instance is shared across test cases in a single file so reset the DOM + while (document.body.firstChild) { + document.body.removeChild(document.body.firstChild); + } + }); + + it('TODO: test case generated by CLI command, please fill in test logic', () => { + // Arrange + const element = createElement('c-triton-demo', { + is: TritonDemo + }); + + // Act + document.body.appendChild(element); + + // Assert + // const div = element.shadowRoot.querySelector('div'); + expect(1).toBe(1); + }); +}); \ No newline at end of file diff --git a/force-app/main/default/lwc/tritonDemo/tritonDemo.html b/force-app/main/default/lwc/tritonDemo/tritonDemo.html new file mode 100644 index 0000000..f5c6b23 --- /dev/null +++ b/force-app/main/default/lwc/tritonDemo/tritonDemo.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/force-app/main/default/lwc/tritonDemo/tritonDemo.js b/force-app/main/default/lwc/tritonDemo/tritonDemo.js new file mode 100644 index 0000000..4753f32 --- /dev/null +++ b/force-app/main/default/lwc/tritonDemo/tritonDemo.js @@ -0,0 +1,41 @@ +import { LightningElement } from 'lwc'; +import { ShowToastEvent } from "lightning/platformShowToastEvent"; + +import triggerDmlException from '@salesforce/apex/TritonDmlDemoController.triggerDmlException'; +import { AREA, TYPE } from 'c/triton'; +import Triton from 'c/triton'; + +export default class TritonDemo extends LightningElement { + + someText; + + async connectedCallback() { + + this.someText = 'Hello World!'; + // This demo will fire everytime this component renders on the page + // meaning every time you load this LWC you will get a log record + try { + const returnMsg = await triggerDmlException(); + } catch(e) { + + const tritonLogger = new Triton(); + tritonLogger.error( + TYPE.FRONTEND, + AREA.ACCOUNTS, + e.body.message, + e.body.stackTrace, + '', + 'c.tritonDemo', + '', + Date.now() + ); + + this.dispatchEvent(new ShowToastEvent({ + message: e.body.message, + title: 'Test error', + variant: {label: 'error', value: 'error' } + })); + } + } +} + \ No newline at end of file diff --git a/force-app/main/default/lwc/tritonDemo/tritonDemo.js-meta.xml b/force-app/main/default/lwc/tritonDemo/tritonDemo.js-meta.xml new file mode 100644 index 0000000..24b2342 --- /dev/null +++ b/force-app/main/default/lwc/tritonDemo/tritonDemo.js-meta.xml @@ -0,0 +1,8 @@ + + + 61.0 + false + + lightning__Tab + + \ No newline at end of file