From 6ed1a2f8d945a05fb2745bf903c862d556c45e98 Mon Sep 17 00:00:00 2001 From: Steve Hetzel Date: Wed, 4 Feb 2026 17:35:48 -0700 Subject: [PATCH 1/6] feat: updates for sending pdpEvents --- LICENSE.txt | 2 +- package.json | 1 + src/o11yReporter.ts | 18 ++++++++++++++- src/o11y_schema_sf_pdp.d.ts | 19 ++++++++++++++++ src/telemetryReporter.ts | 15 ++++++++++++- src/types.ts | 44 +++++++++++++++++++++++++++++++++++++ yarn.lock | 5 +++++ 7 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 src/o11y_schema_sf_pdp.d.ts diff --git a/LICENSE.txt b/LICENSE.txt index ca35d0d..1aeebc5 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ Apache License Version 2.0 -Copyright (c) 2025 Salesforce, Inc. +Copyright (c) 2026 Salesforce, Inc. All rights reserved. Apache License diff --git a/package.json b/package.json index 0bc7789..5600194 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@salesforce/o11y-reporter": "1.7.0", "applicationinsights": "^2.9.8", "got": "^11", + "o11y_schema": "^260.40.0", "proxy-agent": "^6.5.0" }, "devDependencies": { diff --git a/src/o11yReporter.ts b/src/o11yReporter.ts index 7aa4ecb..3492d60 100644 --- a/src/o11yReporter.ts +++ b/src/o11yReporter.ts @@ -14,7 +14,8 @@ * limitations under the License. */ import { O11yService, type BatchingOptions } from '@salesforce/o11y-reporter'; -import { Attributes, O11ySchema, Properties, TelemetryOptions } from './types'; +import { pdpEventSchema } from 'o11y_schema/sf_pdp'; +import { Attributes, O11ySchema, PdpEvent, Properties, TelemetryOptions } from './types'; import { BaseReporter } from './baseReporter'; import { buildPropertiesAndMeasurements } from './utils'; @@ -124,6 +125,21 @@ export class O11yReporter extends BaseReporter { } } + /** + * Sends a PDP event via O11y service. + * + * @param event - PDP event to send. + */ + public async sendPdpEvent(event: PdpEvent): Promise { + await this.initialized; + + this.service.logEventWithSchema(event, pdpEventSchema); + + if (!this._batchingEnabled) { + await this.service.forceFlush(); + } + } + public async flush(): Promise { // Wait for initialization to complete before using the service await this.initialized; diff --git a/src/o11y_schema_sf_pdp.d.ts b/src/o11y_schema_sf_pdp.d.ts new file mode 100644 index 0000000..ffe3941 --- /dev/null +++ b/src/o11y_schema_sf_pdp.d.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2026, Salesforce, Inc. + * + * Licensed 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. + */ +declare module 'o11y_schema/sf_pdp' { + const pdpEventSchema: Record; + export { pdpEventSchema }; +} diff --git a/src/telemetryReporter.ts b/src/telemetryReporter.ts index 51ed0ad..c25d112 100644 --- a/src/telemetryReporter.ts +++ b/src/telemetryReporter.ts @@ -23,7 +23,7 @@ import { ProxyAgent } from 'proxy-agent'; import { AppInsights, TelemetryClient } from './appInsights'; import { isEnabled } from './enabledCheck'; import { O11yReporter } from './o11yReporter'; -import { Attributes, O11ySchema, Properties, TelemetryOptions } from './types'; +import { Attributes, O11ySchema, PdpEvent, Properties, TelemetryOptions } from './types'; /** * This is the main telemetry reporter that should be used by consumers. @@ -176,6 +176,19 @@ export class TelemetryReporter extends AsyncCreatable { } } + /** + * Sends a PDP event via O11y. + * + * @param event - PDP event data to send. + */ + public sendPdpEvent(event: PdpEvent): void { + if (this.isSfdxTelemetryEnabled() && this.enableO11y && this.o11yReporter) { + void this.o11yReporter.sendPdpEvent(event).catch((error) => { + this.logger.debug('Failed to send PDP event to O11y:', error); + }); + } + } + /** * Sends message to both AppInsights and O11y (if enabled). * diff --git a/src/types.ts b/src/types.ts index 3dc0b41..b8e63a9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -35,6 +35,50 @@ export type Attributes = { */ export type O11ySchema = Record; +/** + * PDP Product Feature Taxonomy (PFT) event sent via O11y. + */ +export type PdpEvent = { + /** + * Unique identifier for the event. Follows this naming convention: + * . + * + * object = Specific object within the Product Feature that give us context around the action in lowerCamelCase format. + * Note: The object name can include context around the Product Feature (eg. slackforceMessage). + * Examples: calculatedInsightsRecord,checkoutPaymentmethod, slackforceMessage, promptBuilderTemplate + * + * action = Action the user takes in past tense. This should only be ONE word, in lower case + * Examples: processed, selected, sent, saved + */ + eventName: string; + /** + * Product Feature ID from GUS. + * + * Examples: + * Salesforce CLI = aJCEE0000000mHP4AY + * Salesforce Extensions for VS Code = aJCEE0000000mLm4AI + */ + productFeatureId: string; + /** + * Populate this if there is a unique component with your Event for which a distinct count would be a relevant metric + * E.g., campaignID → count the number of campaigns that were interacted with. + */ + componentId?: string; + /** + * Populate this if there is a unique quantity with your Event for which a sum would be a relevant metric for your + * Product Feature. E.g., rowsProcessed → total Rows processed for Data Streams. + */ + eventVolume?: number; + /** + * Use this field to specify the name of your flexible attribute (eg. experimentId, buttonColor). + */ + contextName?: string; + /** + * Use this field to specify the value of your flexible attribute (eg. exp_123, green). + */ + contextValue?: string; +}; + /** * Batching configuration for O11y telemetry * diff --git a/yarn.lock b/yarn.lock index acfcac3..2adceb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4541,6 +4541,11 @@ o11y_schema@256.154.0: resolved "https://registry.npmjs.org/o11y_schema/-/o11y_schema-256.154.0.tgz#1f2f94f1e42d07e62a3e18e09b026345b057dc0c" integrity sha512-czvU/9cibyZptbr0gLJSM70U7zLlhWC2D2L5e9nOG84Wnqmn4F5YzVjrH1ZQzAzDbBbtbeU6WTS3F/SHqtMQ5g== +o11y_schema@^260.40.0: + version "260.40.0" + resolved "https://registry.yarnpkg.com/o11y_schema/-/o11y_schema-260.40.0.tgz#63478a5022a7715864b2f3fbba425f88c72dec52" + integrity sha512-H9QYuouUs7/Tx9nCbDJ6BGJOkn+dopUG9OWf2jmcSGR0mSANzi3kkpjOo7MajYrWl5K99hDDOQLPs4vPHqwPoA== + object-inspect@^1.13.3, object-inspect@^1.13.4: version "1.13.4" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" From 72d6f7ca3608859a1270e98fffa56097daebdf53 Mon Sep 17 00:00:00 2001 From: Steve Hetzel Date: Wed, 4 Feb 2026 22:19:59 -0700 Subject: [PATCH 2/6] chore: making tsc happy --- src/o11yReporter.ts | 2 ++ test/tsconfig.json | 2 +- tsconfig.json | 2 +- {src => types}/o11y_schema_sf_pdp.d.ts | 0 4 files changed, 4 insertions(+), 2 deletions(-) rename {src => types}/o11y_schema_sf_pdp.d.ts (100%) diff --git a/src/o11yReporter.ts b/src/o11yReporter.ts index 3492d60..96cd67f 100644 --- a/src/o11yReporter.ts +++ b/src/o11yReporter.ts @@ -14,6 +14,8 @@ * limitations under the License. */ import { O11yService, type BatchingOptions } from '@salesforce/o11y-reporter'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore o11y_schema/sf_pdp.d.ts is not a valid module import { pdpEventSchema } from 'o11y_schema/sf_pdp'; import { Attributes, O11ySchema, PdpEvent, Properties, TelemetryOptions } from './types'; import { BaseReporter } from './baseReporter'; diff --git a/test/tsconfig.json b/test/tsconfig.json index dd41c2f..d560be5 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "@salesforce/dev-config/tsconfig-test-strict", - "include": ["unit/**/*.ts", "../node_modules/@types/**/*.d.ts"], + "include": ["unit/**/*.ts", "../node_modules/@types/**/*.d.ts", "../src/**/*.ts", "../**/*.d.ts"], "compilerOptions": { "noEmit": true, "skipLibCheck": true, diff --git a/tsconfig.json b/tsconfig.json index 1755688..360e119 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,5 +8,5 @@ "rootDir": "src", "baseUrl": "." }, - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts", "types/**/*.d.ts"] } diff --git a/src/o11y_schema_sf_pdp.d.ts b/types/o11y_schema_sf_pdp.d.ts similarity index 100% rename from src/o11y_schema_sf_pdp.d.ts rename to types/o11y_schema_sf_pdp.d.ts From 29a20a6a58924b0f433c01fc532c380b62ab0bd0 Mon Sep 17 00:00:00 2001 From: Steve Hetzel Date: Thu, 5 Feb 2026 14:50:43 -0700 Subject: [PATCH 3/6] fix: export PdpEvent --- src/exported.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exported.ts b/src/exported.ts index 132c9b5..e1bdfbd 100644 --- a/src/exported.ts +++ b/src/exported.ts @@ -18,4 +18,4 @@ import { TelemetryReporter } from './telemetryReporter'; export * from './telemetryReporter'; export { isEnabled } from './enabledCheck'; export default TelemetryReporter; -export type { Attributes, O11ySchema, O11yBatchingConfig } from './types'; +export type { Attributes, O11ySchema, O11yBatchingConfig, PdpEvent } from './types'; From f8d76abffdf35cce83ba3497aa9787972628a4c3 Mon Sep 17 00:00:00 2001 From: Steve Hetzel Date: Thu, 5 Feb 2026 15:57:25 -0700 Subject: [PATCH 4/6] chore: addresses review comments --- src/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types.ts b/src/types.ts index b8e63a9..e8be4e5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -50,7 +50,7 @@ export type PdpEvent = { * action = Action the user takes in past tense. This should only be ONE word, in lower case * Examples: processed, selected, sent, saved */ - eventName: string; + eventName: `${string}.${string}`; /** * Product Feature ID from GUS. * @@ -58,10 +58,10 @@ export type PdpEvent = { * Salesforce CLI = aJCEE0000000mHP4AY * Salesforce Extensions for VS Code = aJCEE0000000mLm4AI */ - productFeatureId: string; + productFeatureId: `aJC${string}`; /** * Populate this if there is a unique component with your Event for which a distinct count would be a relevant metric - * E.g., campaignID → count the number of campaigns that were interacted with. + * E.g., CLI plugin command name () or ext command name. */ componentId?: string; /** From 1926b673e0dd3ef617386ecd52ef7f602fdd4fe2 Mon Sep 17 00:00:00 2001 From: Steve Hetzel Date: Fri, 6 Feb 2026 07:52:17 -0700 Subject: [PATCH 5/6] chore: bump to latest o11y-reporter lib --- package.json | 4 ++-- yarn.lock | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 5600194..e54a249 100644 --- a/package.json +++ b/package.json @@ -43,10 +43,10 @@ "dependencies": { "@salesforce/core": "^8.25.0", "@salesforce/kit": "^3.2.4", - "@salesforce/o11y-reporter": "1.7.0", + "@salesforce/o11y-reporter": "1.7.3", "applicationinsights": "^2.9.8", "got": "^11", - "o11y_schema": "^260.40.0", + "o11y_schema": "^260.41.0", "proxy-agent": "^6.5.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 2adceb2..a436fd5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -822,10 +822,10 @@ dependencies: "@salesforce/ts-types" "^2.0.12" -"@salesforce/o11y-reporter@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@salesforce/o11y-reporter/-/o11y-reporter-1.7.0.tgz#9ad4877ab4df18a80e904be564a2063b4563bbe2" - integrity sha512-K4kY3yf2RD2T/RYIID3Ucrlbi9cdR57Lak58o1KLhl4gO+LHMatuk5FJbYpEIuaOqE83G5/pIJ1dOps0BVBcVA== +"@salesforce/o11y-reporter@1.7.3": + version "1.7.3" + resolved "https://registry.yarnpkg.com/@salesforce/o11y-reporter/-/o11y-reporter-1.7.3.tgz#14e58bc511fa3529077eff7e6712182574c52a1c" + integrity sha512-Krd2EgHYrTW1j1Wo5q+4kya6F7WjGvJsD4oUKzefLRw92Pb5b7aMorLFsKMEvLTZSGmTZZClLmQMap0gLOV6qw== dependencies: o11y "^258.7.0" o11y_schema "256.154.0" @@ -4541,10 +4541,10 @@ o11y_schema@256.154.0: resolved "https://registry.npmjs.org/o11y_schema/-/o11y_schema-256.154.0.tgz#1f2f94f1e42d07e62a3e18e09b026345b057dc0c" integrity sha512-czvU/9cibyZptbr0gLJSM70U7zLlhWC2D2L5e9nOG84Wnqmn4F5YzVjrH1ZQzAzDbBbtbeU6WTS3F/SHqtMQ5g== -o11y_schema@^260.40.0: - version "260.40.0" - resolved "https://registry.yarnpkg.com/o11y_schema/-/o11y_schema-260.40.0.tgz#63478a5022a7715864b2f3fbba425f88c72dec52" - integrity sha512-H9QYuouUs7/Tx9nCbDJ6BGJOkn+dopUG9OWf2jmcSGR0mSANzi3kkpjOo7MajYrWl5K99hDDOQLPs4vPHqwPoA== +o11y_schema@^260.41.0: + version "260.41.0" + resolved "https://registry.yarnpkg.com/o11y_schema/-/o11y_schema-260.41.0.tgz#a93c73ad0e8ed57bf3880a2b0bd0d02846ee4c13" + integrity sha512-BJfIvf8Y2rMsz14srDhqSytNe8VG6ljxJOJEB2/ga91RmN0INFZyxQU5Z6E+E62okKUxipXFwI4wkDXt3b2tnA== object-inspect@^1.13.3, object-inspect@^1.13.4: version "1.13.4" From d8c3a9978b7b9c83bb9e07bf9a5d11bce81e899b Mon Sep 17 00:00:00 2001 From: Steve Hetzel Date: Sat, 7 Feb 2026 10:56:59 -0700 Subject: [PATCH 6/6] chore: add unit test --- test/unit/telemetryReporter.test.ts | 40 +++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/unit/telemetryReporter.test.ts b/test/unit/telemetryReporter.test.ts index d4c142f..c4823a9 100644 --- a/test/unit/telemetryReporter.test.ts +++ b/test/unit/telemetryReporter.test.ts @@ -15,11 +15,14 @@ */ import * as os from 'node:os'; import { ConfigAggregator, Logger } from '@salesforce/core'; +import { O11yService } from '@salesforce/o11y-reporter'; import got from 'got'; import { expect } from 'chai'; import * as sinon from 'sinon'; import { AppInsights } from '../../src/appInsights'; +import { O11yReporter } from '../../src/o11yReporter'; import { TelemetryReporter } from '../../src/telemetryReporter'; +import type { PdpEvent } from '../../src/types'; import * as enabledStubs from '../../src/enabledCheck'; describe('TelemetryReporter', () => { @@ -48,6 +51,43 @@ describe('TelemetryReporter', () => { expect(sendStub.calledOnce).to.be.true; }); + it('should send PDPEvent', async () => { + sandbox.stub(ConfigAggregator.prototype, 'getPropertyValue').returns('false'); + + const mockO11yService = { + initialize: sandbox.stub().resolves(), + logEvent: sandbox.stub(), + logEventWithSchema: sandbox.stub(), + forceFlush: sandbox.stub().resolves(), + enableAutoBatching: sandbox.stub().returns(() => {}), + }; + sandbox.stub(O11yService, 'getInstance').returns(mockO11yService as unknown as O11yService); + + const sendPdpEventStub = sandbox.stub(O11yReporter.prototype, 'sendPdpEvent').resolves(); + + const reporter = await TelemetryReporter.create({ + project: 'salesforce-cli', + key: 'not-used', + userId: 'test-user-id-for-pft-testing', + waitForConnection: true, + enableO11y: true, + enableAppInsights: false, + o11yUploadEndpoint: 'https://794testsite.my.site.com/byolwr/webruntime/log/metrics', + }); + + const pdpEvent: PdpEvent = { + eventName: 'salesforceCli.executed', + productFeatureId: 'aJCEE0000000mHP4AY', + componentId: '@salesforce/plugin-auth.org:web:login', + contextName: 'orgId::devhubId', + contextValue: '00Ded000000VsTxEAK::00D460000019MkyEAE', + }; + reporter.sendPdpEvent(pdpEvent); + + expect(sendPdpEventStub.calledOnce).to.be.true; + expect(sendPdpEventStub.firstCall.args[0]).to.deep.equal(pdpEvent); + }); + it('should send a telemetry exception', async () => { const options = { project, key }; sandbox.stub(ConfigAggregator.prototype, 'getPropertyValue').returns('false');