From ac509d53e79fa5a55f0eb11c05105dfe336c39f0 Mon Sep 17 00:00:00 2001 From: Ruchika Sinha <69535463+Ruchika4@users.noreply.github.com> Date: Tue, 24 Feb 2026 11:45:40 -0800 Subject: [PATCH 1/6] [MWPW-186604] Onboard study space (#674) *Onboard study space Resolves: [MWPW-186604](https://jira.corp.adobe.com/browse/MWPW-186604) **Test URLs:** - Before: hhttps://stage--da-dc--adobecom.aem.page/drafts/ruchika/study-space/quiz-maker?unitylibs=stage - After: https://stage--da-dc--adobecom.aem.page/drafts/ruchika/study-space/quiz-maker?unitylibs=dc-study-space Note: DC block is not on stage yet ([PR](https://github.com/adobecom/da-dc/pull/26) open), till then unity flow can be tested here https://ims--da-dc--adobecom.aem.page/acrobat/online/test/unity/study-marquee?unitylibs=dc-study-space Study space on product side is also not on stage, so please test a.com side of features. Also please do a quick sanity for other workflows too as workflow.js has been updated. --------- Co-authored-by: Ruchika Sinha Co-authored-by: Ruchika Sinha Co-authored-by: Cursor --- .../workflow-acrobat/action-binder.test.js | 230 ++++++++++++++++++ .../workflow-acrobat/action-binder.js | 2 + .../workflow/workflow-acrobat/limits.json | 17 ++ .../workflow-acrobat/target-config.json | 48 ++-- unitylibs/core/workflow/workflow.js | 9 +- 5 files changed, 287 insertions(+), 19 deletions(-) diff --git a/test/core/workflow/workflow-acrobat/action-binder.test.js b/test/core/workflow/workflow-acrobat/action-binder.test.js index 430a1c4b..1de88fae 100644 --- a/test/core/workflow/workflow-acrobat/action-binder.test.js +++ b/test/core/workflow/workflow-acrobat/action-binder.test.js @@ -286,6 +286,22 @@ describe('ActionBinder', () => { it('should match image/tiff for pdf-to-png', () => { expect(actionBinder.isSameFileType('pdf-to-png', 'image/tiff')).to.be.true; }); + + it('should return false for quiz-maker with application/pdf', () => { + expect(actionBinder.isSameFileType('quiz-maker', 'application/pdf')).to.be.false; + }); + + it('should return false for quiz-maker with image/jpeg', () => { + expect(actionBinder.isSameFileType('quiz-maker', 'image/jpeg')).to.be.false; + }); + + it('should return false for flashcard-maker with application/pdf', () => { + expect(actionBinder.isSameFileType('flashcard-maker', 'application/pdf')).to.be.false; + }); + + it('should return false for flashcard-maker with image/jpeg', () => { + expect(actionBinder.isSameFileType('flashcard-maker', 'image/jpeg')).to.be.false; + }); }); describe('validateFiles', () => { @@ -988,6 +1004,36 @@ describe('ActionBinder', () => { expect(result).to.be.true; }); + it('should handle redirect for returning user for quiz-maker', async () => { + actionBinder.workflowCfg.enabledFeatures = ['quiz-maker']; + localStorage.setItem('unity.user', 'test-user'); + localStorage.setItem('quiz-maker_attempts', '2'); + + const cOpts = { payload: {} }; + const filesData = { test: 'data' }; + const result = await actionBinder.handleRedirect(cOpts, filesData); + + expect(cOpts.payload.newUser).to.be.false; + expect(cOpts.payload.attempts).to.equal('2+'); + expect(actionBinder.getRedirectUrl.calledWith(cOpts)).to.be.true; + expect(result).to.be.true; + }); + + it('should handle redirect for returning user for flashcard-maker', async () => { + actionBinder.workflowCfg.enabledFeatures = ['flashcard-maker']; + localStorage.setItem('unity.user', 'test-user'); + localStorage.setItem('flashcard-maker_attempts', '2'); + + const cOpts = { payload: {} }; + const filesData = { test: 'data' }; + const result = await actionBinder.handleRedirect(cOpts, filesData); + + expect(cOpts.payload.newUser).to.be.false; + expect(cOpts.payload.attempts).to.equal('2+'); + expect(actionBinder.getRedirectUrl.calledWith(cOpts)).to.be.true; + expect(result).to.be.true; + }); + it('should handle redirect with feedback for multi-file validation failure', async () => { actionBinder.multiFileValidationFailure = true; const cOpts = { payload: {} }; @@ -1211,6 +1257,52 @@ describe('ActionBinder', () => { expect(actionBinder.handleMultiFileUpload.calledWith(validFile)).to.be.true; expect(actionBinder.handleSingleFileUpload.called).to.be.false; }); + + it('should handle verbs that require multi-file upload for quiz-maker', async () => { + actionBinder.workflowCfg = { + name: 'workflow-acrobat', + enabledFeatures: ['quiz-maker'], + targetCfg: { verbsWithoutMfuToSfuFallback: ['compress-pdf', 'quiz-maker'] }, + }; + const files = [ + { name: 'test1.pdf', type: 'application/pdf', size: 1048576 }, + { name: 'test2.pdf', type: 'application/pdf', size: 2097152 }, + ]; + const validFile = [files[0]]; + actionBinder.validateFiles.resolves({ isValid: true, validFiles: validFile }); + + await actionBinder.handleFileUpload(files); + + expect(actionBinder.sanitizeFileName.calledTwice).to.be.true; + expect(actionBinder.filterFilesWithPdflite.called).to.be.true; + expect(actionBinder.validateFiles.called).to.be.true; + expect(actionBinder.initUploadHandler.called).to.be.true; + expect(actionBinder.handleMultiFileUpload.calledWith(validFile)).to.be.true; + expect(actionBinder.handleSingleFileUpload.called).to.be.false; + }); + + it('should handle verbs that require multi-file upload for flashcard-maker', async () => { + actionBinder.workflowCfg = { + name: 'workflow-acrobat', + enabledFeatures: ['flashcard-maker'], + targetCfg: { verbsWithoutMfuToSfuFallback: ['compress-pdf', 'flashcard-maker'] }, + }; + const files = [ + { name: 'test1.pdf', type: 'application/pdf', size: 1048576 }, + { name: 'test2.pdf', type: 'application/pdf', size: 2097152 }, + ]; + const validFile = [files[0]]; + actionBinder.validateFiles.resolves({ isValid: true, validFiles: validFile }); + + await actionBinder.handleFileUpload(files); + + expect(actionBinder.sanitizeFileName.calledTwice).to.be.true; + expect(actionBinder.filterFilesWithPdflite.called).to.be.true; + expect(actionBinder.validateFiles.called).to.be.true; + expect(actionBinder.initUploadHandler.called).to.be.true; + expect(actionBinder.handleMultiFileUpload.calledWith(validFile)).to.be.true; + expect(actionBinder.handleSingleFileUpload.called).to.be.false; + }); }); describe('continueInApp', () => { @@ -1406,6 +1498,64 @@ describe('ActionBinder', () => { spy.restore(); }); + it('should handle input change event with single file for quiz-maker', async () => { + const el = document.createElement('input'); + el.type = 'file'; + const addEventListenerSpy = sinon.spy(el, 'addEventListener'); + const block = { querySelector: sinon.stub().returns(el) }; + const actMap = { input: 'upload' }; + const extractSpy = sinon.spy(actionBinder, 'extractFiles'); + const spy = sinon.spy(actionBinder, 'acrobatActionMaps'); + + await actionBinder.initActionListeners(block, actMap); + + const handler = addEventListenerSpy.getCalls().find((call) => call.args[0] === 'change').args[1]; + actionBinder.signedOut = false; + actionBinder.tokenError = null; + actionBinder.workflowCfg.enabledFeatures = ['quiz-maker']; + + const file = new File(['test content'], 'test.pdf', { type: 'application/pdf' }); + const event = { target: { files: [file], value: '' } }; + + await handler(event); + + expect(extractSpy.called).to.be.true; + expect(spy.called).to.be.true; + expect(spy.firstCall.args).to.deep.equal(['upload', [file], file.size, 'change']); + + spy.restore(); + extractSpy.restore(); + }); + + it('should handle input change event with single file for flashcard-maker', async () => { + const el = document.createElement('input'); + el.type = 'file'; + const addEventListenerSpy = sinon.spy(el, 'addEventListener'); + const block = { querySelector: sinon.stub().returns(el) }; + const actMap = { input: 'upload' }; + const extractSpy = sinon.spy(actionBinder, 'extractFiles'); + const spy = sinon.spy(actionBinder, 'acrobatActionMaps'); + + await actionBinder.initActionListeners(block, actMap); + + const handler = addEventListenerSpy.getCalls().find((call) => call.args[0] === 'change').args[1]; + actionBinder.signedOut = false; + actionBinder.tokenError = null; + actionBinder.workflowCfg.enabledFeatures = ['flashcard-maker']; + + const file = new File(['test content'], 'test.pdf', { type: 'application/pdf' }); + const event = { target: { files: [file], value: '' } }; + + await handler(event); + + expect(extractSpy.called).to.be.true; + expect(spy.called).to.be.true; + expect(spy.firstCall.args).to.deep.equal(['upload', [file], file.size, 'change']); + + spy.restore(); + extractSpy.restore(); + }); + it('should handle input element not found', async () => { const block = { querySelector: sinon.stub().returns(null) }; const actMap = { 'nonexistent-input': 'upload' }; @@ -1612,6 +1762,46 @@ describe('ActionBinder', () => { { code: 'pre_upload_error_missing_verb_config' }, )).to.be.true; }); + + it('should not dispatch error when enabledFeatures[0] is quiz-maker', async () => { + actionBinder.dispatchErrorToast.resetHistory(); + actionBinder.processSingleFile = sinon.stub().resolves(); + actionBinder.processHybrid = sinon.stub().resolves(); + actionBinder.workflowCfg.enabledFeatures = ['quiz-maker']; + const validFiles = [ + { name: 'test.pdf', type: 'application/pdf', size: 1048576 }, + ]; + const totalFileSize = validFiles.reduce((sum, file) => sum + file.size, 0); + await actionBinder.acrobatActionMaps('upload', validFiles, totalFileSize, 'test-event'); + expect(actionBinder.dispatchErrorToast.neverCalledWith( + 'error_generic', + 500, + 'Invalid or missing verb configuration on Unity', + false, + true, + { code: 'pre_upload_error_missing_verb_config' }, + )).to.be.true; + }); + + it('should not dispatch error when enabledFeatures[0] is flashcard-maker', async () => { + actionBinder.dispatchErrorToast.resetHistory(); + actionBinder.processSingleFile = sinon.stub().resolves(); + actionBinder.processHybrid = sinon.stub().resolves(); + actionBinder.workflowCfg.enabledFeatures = ['flashcard-maker']; + const validFiles = [ + { name: 'test.pdf', type: 'application/pdf', size: 1048576 }, + ]; + const totalFileSize = validFiles.reduce((sum, file) => sum + file.size, 0); + await actionBinder.acrobatActionMaps('upload', validFiles, totalFileSize, 'test-event'); + expect(actionBinder.dispatchErrorToast.neverCalledWith( + 'error_generic', + 500, + 'Invalid or missing verb configuration on Unity', + false, + true, + { code: 'pre_upload_error_missing_verb_config' }, + )).to.be.true; + }); }); }); @@ -1776,6 +1966,46 @@ describe('ActionBinder', () => { localStorageStub.restore(); actionBinder.getRedirectUrl.restore(); }); + + it('should handle localStorage access error for quiz-maker', async () => { + actionBinder.workflowCfg.enabledFeatures = ['quiz-maker']; + const localStorageStub = sinon.stub(window.localStorage, 'getItem'); + localStorageStub.throws(new Error('localStorage not available')); + + const cOpts = { payload: {} }; + const filesData = { type: 'application/pdf', size: 123, count: 1 }; + sinon.stub(actionBinder, 'getRedirectUrl').resolves(); + actionBinder.redirectUrl = 'https://test-redirect.com'; + + const result = await actionBinder.handleRedirect(cOpts, filesData); + + expect(result).to.be.true; + expect(cOpts.payload.newUser).to.be.true; + expect(cOpts.payload.attempts).to.equal('1st'); + + localStorageStub.restore(); + actionBinder.getRedirectUrl.restore(); + }); + + it('should handle localStorage access error for flashcard-maker', async () => { + actionBinder.workflowCfg.enabledFeatures = ['flashcard-maker']; + const localStorageStub = sinon.stub(window.localStorage, 'getItem'); + localStorageStub.throws(new Error('localStorage not available')); + + const cOpts = { payload: {} }; + const filesData = { type: 'application/pdf', size: 123, count: 1 }; + sinon.stub(actionBinder, 'getRedirectUrl').resolves(); + actionBinder.redirectUrl = 'https://test-redirect.com'; + + const result = await actionBinder.handleRedirect(cOpts, filesData); + + expect(result).to.be.true; + expect(cOpts.payload.newUser).to.be.true; + expect(cOpts.payload.attempts).to.equal('1st'); + + localStorageStub.restore(); + actionBinder.getRedirectUrl.restore(); + }); }); describe('Experiment Data Integration', () => { diff --git a/unitylibs/core/workflow/workflow-acrobat/action-binder.js b/unitylibs/core/workflow/workflow-acrobat/action-binder.js index d6284907..f0481873 100644 --- a/unitylibs/core/workflow/workflow-acrobat/action-binder.js +++ b/unitylibs/core/workflow/workflow-acrobat/action-binder.js @@ -75,6 +75,8 @@ export default class ActionBinder { 'summarize-pdf': ['single', 'allowed-filetypes-pdf-word-ppt-txt', 'page-limit-600', 'max-filesize-100-mb'], 'pdf-ai': ['hybrid', 'allowed-filetypes-pdf-word-ppt-txt', 'page-limit-600', 'max-numfiles-10', 'max-filesize-100-mb'], 'heic-to-pdf': ['hybrid', 'allowed-filetypes-all', 'allowed-filetypes-heic', 'max-filesize-100-mb'], + 'quiz-maker': ['hybrid', 'allowed-filetypes-study-spaces', 'page-limit-600', 'max-numfiles-100', 'max-filesize-100-mb'], + 'flashcard-maker': ['hybrid', 'allowed-filetypes-study-spaces', 'page-limit-600', 'max-numfiles-100', 'max-filesize-100-mb'], }; static ERROR_MAP = { diff --git a/unitylibs/core/workflow/workflow-acrobat/limits.json b/unitylibs/core/workflow/workflow-acrobat/limits.json index 02993afa..4ac878d5 100644 --- a/unitylibs/core/workflow/workflow-acrobat/limits.json +++ b/unitylibs/core/workflow/workflow-acrobat/limits.json @@ -159,5 +159,22 @@ }, "allowed-filetypes-heic": { "allowedFileTypes": ["image/heic"] + }, + "allowed-filetypes-study-spaces": { + "allowedFileTypes": [ + "application/pdf", + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/msexcel", + "application/vnd.ms-excel", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/x-vnd.oasis.opendocument.spreadsheet", + "application/vnd.ms-powerpoint", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/rtf", + "text/rtf", + "text/plain", + "text/vtt" + ] } } diff --git a/unitylibs/core/workflow/workflow-acrobat/target-config.json b/unitylibs/core/workflow/workflow-acrobat/target-config.json index cde2af50..f5ff4661 100644 --- a/unitylibs/core/workflow/workflow-acrobat/target-config.json +++ b/unitylibs/core/workflow/workflow-acrobat/target-config.json @@ -1,25 +1,12 @@ { - "verb-widget": { - "selector": ".verb-wrapper", + "_defaults": { "renderWidget": false, - "source": ".verb-wrapper .verb-container", - "target": ".verb-wrapper .verb-container", - "showSplashScreen": true, - "splashScreenConfig": { - "fragmentLink": "/dc-shared/fragments/shared-fragments/frictionless/splash-page/splashscreen", - "fragmentLink-acrobat": "/dc-shared/fragments/shared-fragments/frictionless/splash-page/splashscreen-acrobat", - "splashScreenParent": "body" - }, "domainMap": { "acrobat": [ "^(stage\\.)?acrobat\\.adobe\\.com$", "^.+--dc-frictionless--adobecom\\.aem\\.(page|live)$" ] }, - "actionMap": { - ".verb-wrapper": "upload", - "#file-upload": "upload" - }, "uploadLimits": { "HIGH_END": { "files": 10, "chunks": 10 }, "MID_RANGE": { "files": 5, "chunks": 10 }, @@ -28,8 +15,8 @@ "sendSplunkAnalytics": true, "verbsWithoutMfuToSfuFallback": ["compress-pdf"], "nonpdfMfuFeedbackScreenTypeNonpdf": ["combine-pdf"], - "nonpdfSfuProductScreen": ["word-to-pdf", "jpg-to-pdf", "ppt-to-pdf", "excel-to-pdf", "png-to-pdf", "createpdf", "chat-pdf", "chat-pdf-student", "summarize-pdf", "pdf-ai", "heic-to-pdf"], - "mfuUploadAllowed": ["combine-pdf", "rotate-pages", "chat-pdf", "chat-pdf-student", "summarize-pdf", "pdf-ai"], + "nonpdfSfuProductScreen": ["word-to-pdf", "jpg-to-pdf", "ppt-to-pdf", "excel-to-pdf", "png-to-pdf", "createpdf", "chat-pdf", "chat-pdf-student", "summarize-pdf", "pdf-ai", "heic-to-pdf", "quiz-maker", "flashcard-maker"], + "mfuUploadAllowed": ["combine-pdf", "rotate-pages", "chat-pdf", "chat-pdf-student", "summarize-pdf", "pdf-ai", "quiz-maker", "flashcard-maker"], "mfuUploadOnlyPdfAllowed": ["combine-pdf"], "experimentationOn": ["add-comment"], "fetchApiConfig": { @@ -55,5 +42,34 @@ } } } + }, + "verb-widget": { + "selector": ".verb-wrapper", + "source": ".verb-wrapper .verb-container", + "target": ".verb-wrapper .verb-container", + "showSplashScreen": true, + "splashScreenConfig": { + "fragmentLink": "/dc-shared/fragments/shared-fragments/frictionless/splash-page/splashscreen", + "fragmentLink-acrobat": "/dc-shared/fragments/shared-fragments/frictionless/splash-page/splashscreen-acrobat", + "splashScreenParent": "body" + }, + "actionMap": { + ".verb-wrapper": "upload", + "#file-upload": "upload" + } + }, + "study-marquee": { + "selector": ".foreground", + "source": ".foreground .study-marquee-container", + "target": ".foreground .study-marquee-container", + "showSplashScreen": true, + "splashScreenConfig": { + "fragmentLink": "/dc-shared/fragments/shared-fragments/frictionless/splash-page/students-splashscreen", + "splashScreenParent": "body" + }, + "actionMap": { + ".foreground": "upload", + "#file-upload": "upload" + } } } diff --git a/unitylibs/core/workflow/workflow.js b/unitylibs/core/workflow/workflow.js index d1ecfc67..94c7e56a 100644 --- a/unitylibs/core/workflow/workflow.js +++ b/unitylibs/core/workflow/workflow.js @@ -103,7 +103,8 @@ class WfInitiator { async getTarget(rawTargetConfig) { const targetConfig = await rawTargetConfig.json(); const prevElem = this.el.previousElementSibling; - const supportedBlocks = Object.keys(targetConfig); + const supportedBlocks = Object.keys(targetConfig).filter((key) => !key.startsWith('_')); + const defaults = targetConfig._defaults || {}; let targetCfg = null; for (let k = 0; k < supportedBlocks.length; k += 1) { const classes = supportedBlocks[k].split('.'); @@ -118,7 +119,7 @@ class WfInitiator { } } if (hasAllClasses) { - targetCfg = targetConfig[supportedBlocks[k]]; + targetCfg = { ...defaults, ...targetConfig[supportedBlocks[k]] }; break; } } @@ -208,6 +209,8 @@ class WfInitiator { 'summarize-pdf', 'pdf-ai', 'heic-to-pdf', + 'quiz-maker', + 'flashcard-maker', ]), }, 'workflow-ai': { @@ -241,7 +244,7 @@ class WfInitiator { getEnabledFeatures() { const { supportedFeatures, supportedTexts } = this.workflowCfg; - const verbWidget = this.el.closest('.section')?.querySelector('.verb-widget'); + const verbWidget = this.el.closest('.section')?.querySelector('.verb-widget, .study-marquee'); if (verbWidget) { const verb = [...verbWidget.classList].find((cn) => supportedFeatures.has(cn)); if (verb) this.workflowCfg.enabledFeatures.push(verb); From df6c96e0b239a6ffdef71f9bfd973bc4afc1a3c2 Mon Sep 17 00:00:00 2001 From: Sanjay Saravanan <75960494+sanjayms01@users.noreply.github.com> Date: Mon, 2 Mar 2026 22:24:27 -0800 Subject: [PATCH 2/6] MWPW-188830 Temp change to validate file extension for HEIC file type (#678) * [Temporary] Updating our file type validation such that for the HEIC verb, we will perform a file extension check * This logic will be updated to be generalized for all verbs later with this ticket: [MWPW-182430](https://jira.corp.adobe.com/browse/MWPW-182430) Resolves: [MWPW-188830](https://jira.corp.adobe.com/browse/MWPW-188830) **Test URLs:** - Before: https://stage--dc-frictionless--adobecom.aem.page/heic-to-pdf?unitylibs=stage - After: https://stage--dc-frictionless--adobecom.aem.page/heic-to-pdf?unitylibs=MWPW-188830 --- .../workflow/workflow-acrobat/action-binder.js | 6 +++++- .../workflow/workflow-acrobat/upload-handler.js | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/unitylibs/core/workflow/workflow-acrobat/action-binder.js b/unitylibs/core/workflow/workflow-acrobat/action-binder.js index f0481873..ee604231 100644 --- a/unitylibs/core/workflow/workflow-acrobat/action-binder.js +++ b/unitylibs/core/workflow/workflow-acrobat/action-binder.js @@ -376,7 +376,11 @@ export default class ActionBinder { for (const file of files) { let fail = false; - if (!this.limits.allowedFileTypes.includes(file.type)) { + const { getExtension } = await import('../../../utils/FileUtils.js'); + const typeCheckFail = this.workflowCfg.enabledFeatures[0] === 'heic-to-pdf' + ? getExtension(file.name).toLowerCase() !== 'heic' && !this.limits.allowedFileTypes.includes(file.type) + : !this.limits.allowedFileTypes.includes(file.type); + if (typeCheckFail) { let errorMessage = errorMessages.UNSUPPORTED_TYPE; if (this.isSameFileType(this.workflowCfg.enabledFeatures[0], file.type)) errorMessage = errorMessages.SAME_FILE_TYPE; if (this.MULTI_FILE) { diff --git a/unitylibs/core/workflow/workflow-acrobat/upload-handler.js b/unitylibs/core/workflow/workflow-acrobat/upload-handler.js index a79a74d3..ba04bef4 100644 --- a/unitylibs/core/workflow/workflow-acrobat/upload-handler.js +++ b/unitylibs/core/workflow/workflow-acrobat/upload-handler.js @@ -19,13 +19,22 @@ export default class UploadHandler { return feature === 'pdf-ai' ? 'chat-pdf-pdf-ai' : feature; } + async getEffectiveFileType(file) { + const { getExtension } = await import('../../../utils/FileUtils.js'); + const isHeicWithoutMimeType = this.actionBinder.workflowCfg.enabledFeatures[0] === 'heic-to-pdf' + && getExtension(file.name).toLowerCase() === 'heic' + && !file.type; + return isHeicWithoutMimeType ? 'image/heic' : file.type; + } + async createAsset(file, multifile = false, workflowId = null) { + const effectiveFileType = await this.getEffectiveFileType(file); const data = { surfaceId: unityConfig.surfaceId, targetProduct: this.actionBinder.workflowCfg.productName, name: file.name, size: file.size, - format: file.type, + format: effectiveFileType, ...(multifile && { multifile }), ...(workflowId && { workflowId }), }; @@ -346,6 +355,7 @@ export default class UploadHandler { } fileData.assetId = assetData.id; this.actionBinder.setAssetId(assetData.id); + const effectiveFileType = await this.getEffectiveFileType(file); cOpts = { assetId: assetData.id, targetProduct: this.actionBinder.workflowCfg.productName, @@ -357,7 +367,7 @@ export default class UploadHandler { [assetData.id]: { name: file.name, size: file.size, - type: file.type, + type: effectiveFileType, }, }, ...(!isPdf ? { feedback: 'nonpdf' } : {}), @@ -372,7 +382,7 @@ export default class UploadHandler { ({ failedFiles, attemptMap } = await this.chunkPdf( [assetData], [blobData], - [file.type], + [effectiveFileType], maxConcurrentChunks, abortSignal, )); From dbfd6fce9ea94fcee29ebfc2c539f1eb531d1592 Mon Sep 17 00:00:00 2001 From: Vipul Gupta Date: Tue, 3 Mar 2026 15:18:13 +0530 Subject: [PATCH 3/6] [MWPW-188359] Adding Unity FE support for firefly project Doodlebug Lite (#682) This PR includes below changes 1. Support in target config and other files for integrating new upload-marquee block 2. Added support for light dark theme in splash screen. The logic is basis the theme class passed in the unity block. 3. Additional Analytics support for sending unity analytics for the Edit photos CTA 4. Unity FE handoff logic for doodlebug Resolves: [MWPW-188359](https://jira.corp.adobe.com/browse/MWPW-188359) **Test URLs:** - Before: https://stage--cc--adobecom.aem.page/drafts/doodlebug/remove-object - After: https://stage--cc--adobecom.aem.page/drafts/doodlebug/remove-object?unitylibs=doodlebug Testing Notes: 1. Validate E2E workflow for project doodlebug 2. Validate added analytics and error scenarios 3. Validate the final redirect firefly URL 4. Validate dark splash screen for doodlebug 5. Validate already existing FF pages that they should not have any changes in behaviour/splashscreen 6. Validate existing upload workflows for other products like ps, lr and expect no change in them --------- Co-authored-by: Sanjay Saravanan <75960494+sanjayms01@users.noreply.github.com> Co-authored-by: vipulg Co-authored-by: Arushi Gupta <65466846+arugupta1992@users.noreply.github.com> Co-authored-by: Arushi Gupta Co-authored-by: Arushi Gupta --- unitylibs/core/styles/splash-screen.css | 21 ++++++++ unitylibs/core/styles/styles.css | 48 ++++++++++++------- .../workflow/workflow-upload/action-binder.js | 18 ++++++- .../workflow-upload/target-config.json | 10 ++-- unitylibs/core/workflow/workflow.js | 2 +- unitylibs/scripts/transition-screen.js | 4 ++ 6 files changed, 81 insertions(+), 22 deletions(-) diff --git a/unitylibs/core/styles/splash-screen.css b/unitylibs/core/styles/splash-screen.css index 4826160d..5e2baab3 100644 --- a/unitylibs/core/styles/splash-screen.css +++ b/unitylibs/core/styles/splash-screen.css @@ -51,6 +51,15 @@ body > .splash-loader { display: flex; } +.splash-loader.dark .text .con-button { + border-color: white !important; + color: white !important; +} + +.splash-loader.dark .text .con-button:hover { + color: black !important; +} + .progress-holder { width: 100%; } @@ -129,6 +138,10 @@ body > .splash-loader { font-weight: 700; } +.splash-loader.dark .progress-holder .spectrum-ProgressBar--sideLabel .spectrum-ProgressBar-percentage { + color: white !important; +} + .splash-loader .text .icon-area img, .splash-loader .text video { width: 157px; @@ -148,6 +161,10 @@ body > .splash-loader { margin: 0 0 var(--spacing-l) 0; } +.splash-loader.dark .text-block [class^="heading"]:first-of-type { + color: white !important; +} + .splash-loader .text-block p.icon-area { margin-block: var(--spacing-s); } @@ -167,6 +184,10 @@ body > .splash-loader { font-weight: 700; } +.splash-loader.dark .text-block p { + color: white !important; +} + @media screen and (min-width: 600px) { .splash-loader h1, .splash-loader h2, diff --git a/unitylibs/core/styles/styles.css b/unitylibs/core/styles/styles.css index 9b7c3d4e..03ebc4a9 100644 --- a/unitylibs/core/styles/styles.css +++ b/unitylibs/core/styles/styles.css @@ -174,7 +174,8 @@ } .unity-enabled .interactive-area .alert-holder, -.upload.unity-enabled .alert-holder { +.upload.unity-enabled .alert-holder, +.upload-marquee.unity-enabled .alert-holder { display: none; justify-content: center; align-items: center; @@ -188,12 +189,14 @@ } .unity-enabled .interactive-area .alert-holder.show, -.upload.unity-enabled .alert-holder.show { +.upload.unity-enabled .alert-holder.show, +.upload-marquee.unity-enabled .alert-holder.show { display: flex; } .unity-enabled .interactive-area .alert-holder .alert-toast, -.upload.unity-enabled .alert-holder .alert-toast { +.upload.unity-enabled .alert-holder .alert-toast, +.upload-marquee.unity-enabled .alert-holder .alert-toast { background: var(--alert-red); border-radius: 10px; width: 339px; @@ -206,14 +209,16 @@ } .unity-enabled .interactive-area .alert-holder .alert-toast .alert-content, -.upload.unity-enabled .alert-holder .alert-toast .alert-content { +.upload.unity-enabled .alert-holder .alert-toast .alert-content, +.upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content { display: flex; flex-direction: row; align-items: center; } .unity-enabled .interactive-area .alert-holder .alert-toast .alert-content .alert-icon, -.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-icon { +.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-icon, +.upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content .alert-icon { display: flex; width: auto; align-items: center; @@ -222,7 +227,8 @@ .unity-enabled .interactive-area .alert-holder .alert-toast .alert-content .alert-icon svg, .unity-enabled .interactive-area .alert-holder .alert-toast .alert-content .alert-icon img, -.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-icon svg{ +.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-icon svg, +.upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content .alert-icon svg { display: flex; align-items: center; height: 20px; @@ -237,12 +243,14 @@ [dir="rtl"] .unity-enabled .interactive-area .alert-holder .alert-toast .alert-content .alert-icon svg, [dir="rtl"] .unity-enabled .interactive-area .alert-holder .alert-toast .alert-content .alert-icon img, -[dir="rtl"] .upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-icon svg { +[dir="rtl"] .upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-icon svg, +[dir="rtl"] .upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content .alert-icon svg { padding: 18px 16px 18px 0; } .unity-enabled .interactive-area .alert-holder .alert-toast .alert-content .alert-text, -.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-text { +.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-text, +.upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content .alert-text { width: 239px; min-height: 18px; padding-left: 12px; @@ -251,7 +259,8 @@ align-self: stretch; } -.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-text { +.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-text, +.upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content .alert-text { align-content: center; } @@ -263,7 +272,8 @@ margin: 12px 0; } -.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-text p { +.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-text p, +.upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content .alert-text p { color: var(--color-white); font-weight: 400; font-size: var(--type-body-xs-size); @@ -272,7 +282,8 @@ } .unity-enabled .interactive-area .alert-holder .alert-toast .alert-content .alert-close, -.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-close{ +.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-close, +.upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content .alert-close { display: flex; justify-content: center; align-items: center; @@ -284,12 +295,14 @@ } [dir="rtl"] .unity-enabled .interactive-area .alert-holder .alert-toast .alert-content .alert-close, -.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-close{ +.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-close, +.upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content .alert-close { padding: 12px 0 12px 8px; } .unity-enabled .interactive-area .alert-holder .alert-toast .alert-content .alert-close svg, -.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-close svg { +.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-close svg, +.upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content .alert-close svg { display: flex; height: 12px; min-height: 12px; @@ -300,7 +313,8 @@ } .unity-enabled .interactive-area .alert-holder .alert-toast .alert-content .alert-close .alert-close-text, -.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-close .alert-close-text{ +.upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-close .alert-close-text, +.upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content .alert-close .alert-close-text { display: none; } @@ -344,11 +358,13 @@ } @media screen and (max-width: 600px) { - .upload.unity-enabled .alert-holder .alert-toast { + .upload.unity-enabled .alert-holder .alert-toast, + .upload-marquee.unity-enabled .alert-holder .alert-toast { width: 276px; } - .upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-text { + .upload.unity-enabled .alert-holder .alert-toast .alert-content .alert-text, + .upload-marquee.unity-enabled .alert-holder .alert-toast .alert-content .alert-text { width: 179px; } diff --git a/unitylibs/core/workflow/workflow-upload/action-binder.js b/unitylibs/core/workflow/workflow-upload/action-binder.js index 0dcbc6e9..1c4317b9 100644 --- a/unitylibs/core/workflow/workflow-upload/action-binder.js +++ b/unitylibs/core/workflow/workflow-upload/action-binder.js @@ -258,6 +258,13 @@ export default class ActionBinder { } } + getVerbFromDom() { + const verbEl = this.unityEl?.querySelector('[class*="icon-verb-"]'); + if (!verbEl) return undefined; + const verbClass = Array.from(verbEl.classList).find((cls) => cls.startsWith('icon-verb-')); + return verbClass?.slice('icon-verb-'.length); + } + async continueInApp(assetId, file) { const { getCgenQueryParams } = await import(`${getUnityLibs()}/utils/cgen-utils.js`); const queryParams = getCgenQueryParams(this.unityEl); @@ -269,6 +276,8 @@ export default class ActionBinder { }; if (this.workflowCfg.productName.toLowerCase() === 'firefly') { payload.action = 'asset-upload'; + const verb = this.getVerbFromDom(); + if (verb) payload.verb = verb; } if (this.workflowCfg.productName.toLowerCase() === 'photoshop') { payload.referer = window.location.href; @@ -384,6 +393,7 @@ export default class ActionBinder { async loadTransitionScreen() { if (!this.transitionScreen) { try { + this.workflowCfg.theme = this.unityEl.classList.contains('dark') ? 'dark' : null; const { default: TransitionScreen } = await import(`${getUnityLibs()}/scripts/transition-screen.js`); this.transitionScreen = new TransitionScreen(this.splashScreenEl, this.initActionListeners, this.LOADER_LIMIT, this.workflowCfg, this.desktop); await this.transitionScreen.delayedSplashLoader(); @@ -406,6 +416,9 @@ export default class ActionBinder { case 'interrupt': await this.cancelUploadOperation(); break; + case 'redirect': + this.logAnalyticsinSplunk('Edit Photos CTA|UnityWidget', {}); + break; default: break; } @@ -423,8 +436,9 @@ export default class ActionBinder { const actions = { A: (el, key) => { el.addEventListener('click', async (e) => { - e.preventDefault(); - await this.executeActionMaps(actMap[key]); + const action = actMap[key]; + if (action !== 'redirect') e.preventDefault(); + await this.executeActionMaps(action); }); }, DIV: (el, key) => { diff --git a/unitylibs/core/workflow/workflow-upload/target-config.json b/unitylibs/core/workflow/workflow-upload/target-config.json index 49290d17..0648b0df 100644 --- a/unitylibs/core/workflow/workflow-upload/target-config.json +++ b/unitylibs/core/workflow/workflow-upload/target-config.json @@ -1,5 +1,5 @@ { - "upload": { + "_defaults": { "selector": ".drop-zone", "renderWidget": false, "source": ".drop-zone", @@ -33,12 +33,16 @@ "fragmentLink-photoshop": "/cc-shared/fragments/products/photoshop/unity/splash-page/splashscreen", "fragmentLink-lightroom": "/cc-shared/fragments/products/photoshop-lightroom/unity/splash-page/splashscreen", "fragmentLink-firefly": "/cc-shared/fragments/products/firefly/unity/splash-page", + "fragmentLink-firefly-dark": "/cc-shared/fragments/products/firefly/unity/splash-page-dark", "splashScreenParent": "body" }, "actionMap": { ".drop-zone": "upload", - "#file-upload": "upload" + "#file-upload": "upload", + ".upload-marquee-cta": "redirect" }, "sendSplunkAnalytics": true - } + }, + "upload": {}, + "upload-marquee": {} } diff --git a/unitylibs/core/workflow/workflow.js b/unitylibs/core/workflow/workflow.js index 94c7e56a..f58ed53b 100644 --- a/unitylibs/core/workflow/workflow.js +++ b/unitylibs/core/workflow/workflow.js @@ -148,7 +148,7 @@ class WfInitiator { const newPic = asset.cloneNode(true); this.el.querySelector(':scope > div > div').prepend(newPic); } - if (!targetCfg.renderWidget && block.classList.contains('upload')) { + if (!targetCfg.renderWidget && (block.classList.contains('upload') || block.classList.contains('upload-marquee'))) { return block.querySelectorAll(selector); } if (!targetCfg.renderWidget) return null; diff --git a/unitylibs/scripts/transition-screen.js b/unitylibs/scripts/transition-screen.js index 36b54e1c..8af62181 100644 --- a/unitylibs/scripts/transition-screen.js +++ b/unitylibs/scripts/transition-screen.js @@ -82,6 +82,9 @@ export default class TransitionScreen { } const productName = this.workflowCfg.productName.toLowerCase(); if (this.workflowCfg.name === 'workflow-upload') { + const { theme } = this.workflowCfg; + const themedKey = theme ? `fragmentLink-${productName}-${theme}` : null; + if (themedKey && splashScreenConfig[themedKey]) return splashScreenConfig[themedKey]; return splashScreenConfig[`fragmentLink-${productName}`]; } return splashScreenConfig.fragmentLink; @@ -107,6 +110,7 @@ export default class TransitionScreen { } const sections = doc.querySelectorAll('body > div'); const f = createTag('div', { class: 'fragment splash-loader decorate', style: 'display: none', tabindex: '-1', role: 'dialog', 'aria-modal': 'true' }); + if (this.workflowCfg.theme === 'dark') f.classList.add('dark'); f.append(...sections); const splashDiv = document.querySelector( this.workflowCfg.targetCfg.splashScreenConfig.splashScreenParent, From 8fa31a435ffbbece787e153580a5b1f20da81d8f Mon Sep 17 00:00:00 2001 From: Arushi Gupta <65466846+arugupta1992@users.noreply.github.com> Date: Tue, 3 Mar 2026 16:34:29 +0530 Subject: [PATCH 4/6] [MWPW-182868,MWPW-188630] Removing the logging when target data not found (#677) Testing Notes: Basic sanity required. Pick pdf-editor for the sanity and do it on US vpn to trigger the challenger workflow. Resolves: https://jira.corp.adobe.com/browse/MWPW-182868 https://jira.corp.adobe.com/browse/MWPW-188630 **Test URLs:** - Before: https://main--dc--adobecom.aem.live/acrobat/online/pdf-editor?unitylibs=stage - After: https://main--dc--adobecom.aem.live/acrobat/online/pdf-editor?unitylibs=mwpw182868 --------- Co-authored-by: Arushi Gupta --- test/utils/experiment-provider.test.js | 45 ++++++++++++-------------- unitylibs/utils/experiment-provider.js | 4 +-- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/test/utils/experiment-provider.test.js b/test/utils/experiment-provider.test.js index 0d74b789..4dfd546b 100644 --- a/test/utils/experiment-provider.test.js +++ b/test/utils/experiment-provider.test.js @@ -80,25 +80,22 @@ describe('getExperimentData', () => { expect(result).to.deep.equal(mockTargetData); }); - it('should reject when target returns empty decisions', async () => { - await testErrorScenario( - 'Target proposition returned but no valid data for scopes: acom_unity_acrobat_add-comment_us', - () => setupMock(createMockResult(null, [])), - ); + it('should resolve with null when target returns empty decisions', async () => { + setupMock(createMockResult(null, [])); + const result = await getExperimentData(['acom_unity_acrobat_add-comment_us']); + expect(result).to.equal(null); }); - it('should reject when target returns empty items', async () => { - await testErrorScenario( - 'Target proposition returned but no valid data for scopes: acom_unity_acrobat_add-comment_us', - () => setupMock({ decisions: [{ items: [] }] }), - ); + it('should resolve with null when target returns empty items', async () => { + setupMock({ decisions: [{ items: [] }] }); + const result = await getExperimentData(['acom_unity_acrobat_add-comment_us']); + expect(result).to.equal(null); }); - it('should reject when target returns invalid structure', async () => { - await testErrorScenario( - 'Target proposition returned but no valid data for scopes: acom_unity_acrobat_add-comment_us', - () => setupMock({ invalid: 'structure' }), - ); + it('should resolve with null when target returns invalid structure', async () => { + setupMock({ invalid: 'structure' }); + const result = await getExperimentData(['acom_unity_acrobat_add-comment_us']); + expect(result).to.equal(null); }); it('should reject when satellite track throws exception', async () => { @@ -108,18 +105,16 @@ describe('getExperimentData', () => { ); }); - it('should reject when target result is null', async () => { - await testErrorScenario( - 'Target proposition returned but no valid data for scopes: acom_unity_acrobat_add-comment_us', - () => setupMock(null), - ); + it('should resolve with null when target result is null', async () => { + setupMock(null); + const result = await getExperimentData(['acom_unity_acrobat_add-comment_us']); + expect(result).to.equal(null); }); - it('should reject when target result is undefined', async () => { - await testErrorScenario( - 'Target proposition returned but no valid data for scopes: acom_unity_acrobat_add-comment_us', - () => setupMock(undefined), - ); + it('should resolve with null when target result is undefined', async () => { + setupMock(undefined); + const result = await getExperimentData(['acom_unity_acrobat_add-comment_us']); + expect(result).to.equal(null); }); }); diff --git a/unitylibs/utils/experiment-provider.js b/unitylibs/utils/experiment-provider.js index 3a60c073..32d54701 100644 --- a/unitylibs/utils/experiment-provider.js +++ b/unitylibs/utils/experiment-provider.js @@ -32,10 +32,8 @@ export async function getExperimentData(decisionScopes) { const targetData = TargetPropositionResult?.decisions?.[0]?.items?.[0]?.data?.content; if (targetData) { window._satellite.track('propositionDisplay', TargetPropositionResult.propositions); - resolve(targetData); - } else { - reject(new Error(`Target proposition returned but no valid data for scopes: ${Array.isArray(decisionScopes) ? decisionScopes.join(', ') : decisionScopes}`)); } + resolve(targetData || null); }, }); } catch (e) { From 68bb3e9aa3c2d2aeb13e8080fb3398a11119e831 Mon Sep 17 00:00:00 2001 From: Manasvi Agrawal Date: Thu, 5 Mar 2026 10:57:07 +0530 Subject: [PATCH 5/6] [MWPW-186021] fix(LANA): Resolve pre_upload_error_fetching_access_token errors caused by IMS token fetch failures (#685) * Fix error message serialization for `pre_upload_error_fetching_access_token` error code * Previously, the LANA log was logging the error object as `[object Object]`, making it unreadable: `Error Code: pre_upload_error_fetching_access_token, Status: null, Message: Unable to process the request, Info: Upload Type: single; Could not fetch access token; Error: [object Object], Account Type: signed-in` * Wrapped `originalError` with `JSON.stringify` so the full error details are now captured in the log * Treat IMS `invalid_credentials` error as a guest user instead of a fatal error, allowing the flow to continue gracefully Resolves: [MWPW-186021](https://jira.corp.adobe.com/browse/MWPW-186021) **Test URLs:** - Before: https://stage--dc--adobecom.aem.live/drafts/nala/acrobat/online/test/compress-pdf/?martech=off - After: https://stage--dc--adobecom.aem.live/drafts/nala/acrobat/online/test/compress-pdf?unitylibs=MWPW-186021&martech=off --- unitylibs/core/workflow/workflow-acrobat/action-binder.js | 2 +- unitylibs/scripts/utils.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/unitylibs/core/workflow/workflow-acrobat/action-binder.js b/unitylibs/core/workflow/workflow-acrobat/action-binder.js index ee604231..fd08b69e 100644 --- a/unitylibs/core/workflow/workflow-acrobat/action-binder.js +++ b/unitylibs/core/workflow/workflow-acrobat/action-binder.js @@ -668,7 +668,7 @@ export default class ActionBinder { if (this.signedOut === undefined) { if (this.tokenError) { const errorDetails = this.tokenError; - await this.dispatchErrorToast('pre_upload_error_fetching_access_token', null, `Could not fetch access token; Error: ${errorDetails.originalError}`, false, true, { + await this.dispatchErrorToast('pre_upload_error_fetching_access_token', null, `Could not fetch access token; Error: ${JSON.stringify(errorDetails.originalError)}`, false, true, { code: 'pre_upload_error_fetching_access_token', desc: errorDetails, }); diff --git a/unitylibs/scripts/utils.js b/unitylibs/scripts/utils.js index 5e995ce5..57981310 100644 --- a/unitylibs/scripts/utils.js +++ b/unitylibs/scripts/utils.js @@ -44,6 +44,13 @@ async function getRefreshToken() { const { tokenInfo } = window.adobeIMS ? await window.adobeIMS.refreshToken() : {}; return tokenInfo; } catch (e) { + const errorMsg = (e?.message || e?.exception?.message || '').trim(); + if (errorMsg === 'invalid_credentials') { + return { + token: null, + isGuestToken: true + }; + } return { token: null, error: e, From 9a7f8fab7ebc98358d8022ec31256cd4dc779f44 Mon Sep 17 00:00:00 2001 From: Arushi Gupta <65466846+arugupta1992@users.noreply.github.com> Date: Thu, 5 Mar 2026 11:14:28 +0530 Subject: [PATCH 6/6] Changes to send verb name and action in the workflow firefly-upload analytics (#684) **Test URLs:** - Before: https://doodlebug--cc--adobecom.aem.page/drafts/doodlebug/remove-object?unitylibs=stage - After: https://doodlebug--cc--adobecom.aem.page/drafts/doodlebug/remove-object?unitylibs=db-analytics Co-authored-by: Arushi Gupta --- unitylibs/core/workflow/workflow-upload/action-binder.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unitylibs/core/workflow/workflow-upload/action-binder.js b/unitylibs/core/workflow/workflow-upload/action-binder.js index 1c4317b9..91c43016 100644 --- a/unitylibs/core/workflow/workflow-upload/action-binder.js +++ b/unitylibs/core/workflow/workflow-upload/action-binder.js @@ -85,6 +85,7 @@ export default class ActionBinder { this.sendAnalyticsToSplunk = null; this.assetId = null; this.filesData = {}; + this.verb = this.getVerbFromDom(); } getApiConfig() { @@ -276,8 +277,7 @@ export default class ActionBinder { }; if (this.workflowCfg.productName.toLowerCase() === 'firefly') { payload.action = 'asset-upload'; - const verb = this.getVerbFromDom(); - if (verb) payload.verb = verb; + if (this.verb) payload.verb = this.verb; } if (this.workflowCfg.productName.toLowerCase() === 'photoshop') { payload.referer = window.location.href; @@ -349,7 +349,7 @@ export default class ActionBinder { logAnalyticsinSplunk(eventName, data) { if (this.sendAnalyticsToSplunk) { - this.sendAnalyticsToSplunk(eventName, this.workflowCfg.productName, data, `${unityConfig.apiEndPoint}/log`); + this.sendAnalyticsToSplunk(eventName, this.workflowCfg.productName, { ...data, action: 'upload', verb: this.verb }, `${unityConfig.apiEndPoint}/log`); } }