From 0d52acf9eaa9a2c170459f03e9edad25375f4c1c Mon Sep 17 00:00:00 2001 From: Jordan Porter Date: Mon, 5 Aug 2024 10:20:57 -0600 Subject: [PATCH] feat: Report Page Actions with Generic Events Feature (#1124) --- README.md | 4 +- docs/warning-codes.md | 4 +- src/cdn/experimental.js | 4 - src/cdn/polyfills/pro.js | 2 - src/cdn/polyfills/spa.js | 2 - src/cdn/pro.js | 2 - src/cdn/spa.js | 2 - src/common/config/state/init.js | 4 +- .../generic_events/aggregate/index.js | 47 ++++-- .../generic_events/instrument/index.js | 10 +- src/features/page_action/aggregate/index.js | 113 ------------- src/features/page_action/instrument/index.js | 12 +- .../aggregate/initialized-features.js | 2 +- src/features/utils/lazy-feature-loader.js | 2 - src/index.js | 1 + src/loaders/agent-base.js | 4 - src/loaders/api/api.js | 2 +- src/loaders/api/apiAsync.js | 2 +- src/loaders/browser-agent.js | 2 - src/loaders/configure/configure.js | 6 +- src/loaders/features/features.js | 12 +- src/loaders/micro-agent.js | 2 +- tests/assets/instrumented-manual.html | 2 +- tests/components/api.test.js | 2 +- tests/components/apiAsync.test.js | 4 +- tests/components/contextual-ee.test.js | 16 +- .../generic_events/aggregate/index.test.js | 148 ++++++++++++++++++ .../generic_events/instrument/index.test.js | 51 ++++++ tests/specs/cleanup-on-halt.e2e.js | 2 +- tests/specs/ins/harvesting.e2e.js | 12 +- tests/specs/npm/index.e2e.js | 3 +- tests/unit/common/config/state/init.test.js | 16 +- .../utils/lazy-feature-loader.test.js | 11 +- tests/unit/loaders/browser-agent.test.js | 2 - .../custom-agent-pro-deprecated-features.js | 23 +++ .../src/custom-agent-pro.js | 2 - .../src/custom-agent-spa.js | 2 - .../browser-agent-wrapper/src/worker-agent.js | 4 +- .../browser-agent-wrapper/webpack.config.js | 7 + .../custom-agent-pro-deprecated-features.js | 23 +++ .../raw-src-wrapper/src/custom-agent-pro.js | 2 - .../raw-src-wrapper/src/custom-agent-spa.js | 2 - .../raw-src-wrapper/src/worker-agent.js | 4 +- .../raw-src-wrapper/webpack.config.js | 7 + .../types-resolver/custom-agent.ts | 2 +- tools/testing-server/constants.js | 2 +- 46 files changed, 375 insertions(+), 215 deletions(-) delete mode 100644 src/features/page_action/aggregate/index.js create mode 100644 tests/components/generic_events/aggregate/index.test.js create mode 100644 tests/components/generic_events/instrument/index.test.js create mode 100644 tools/test-builds/browser-agent-wrapper/src/custom-agent-pro-deprecated-features.js create mode 100644 tools/test-builds/raw-src-wrapper/src/custom-agent-pro-deprecated-features.js diff --git a/README.md b/README.md index f75263689..d08848219 100644 --- a/README.md +++ b/README.md @@ -78,9 +78,9 @@ const options = { The following features may be disabled by adding `init` entries as shown above. Note that the `page_view_event` feature may not be disabled. - `ajax` +- `generic_events` - `jserrors` - `metrics` -- `page_action` - `page_view_timing` - `session_replay` - `session_trace` @@ -125,7 +125,7 @@ The following feature modules are available for inclusion in the `features` arra import { Ajax } from '@newrelic/browser-agent/features/ajax'; import { JSErrors } from '@newrelic/browser-agent/features/jserrors'; import { Metrics } from '@newrelic/browser-agent/features/metrics'; -import { PageAction } from '@newrelic/browser-agent/features/page_action'; +import { GenericEvents } from '@newrelic/browser-agent/features/generic_events'; import { PageViewEvent } from '@newrelic/browser-agent/features/page_view_event'; import { PageViewTiming } from '@newrelic/browser-agent/features/page_view_timing'; import { SessionTrace } from '@newrelic/browser-agent/features/session_trace'; diff --git a/docs/warning-codes.md b/docs/warning-codes.md index ddb1153c8..61c44f990 100644 --- a/docs/warning-codes.md +++ b/docs/warning-codes.md @@ -86,4 +86,6 @@ ### 42 `Failed to execute setApplicationVersion. Expected ` ### 43 -`Agent not configured properly.` \ No newline at end of file +`Agent not configured properly.` +### 44 +`Invalid object passed to generic event aggregate. Missing "eventType".` \ No newline at end of file diff --git a/src/cdn/experimental.js b/src/cdn/experimental.js index f0ab476c9..ce27c50be 100644 --- a/src/cdn/experimental.js +++ b/src/cdn/experimental.js @@ -18,9 +18,7 @@ import { Instrument as InstrumentXhr } from '../features/ajax/instrument' import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument' import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument' import { Instrument as InstrumentGenericEvents } from '../features/generic_events/instrument' -// import { Instrument as InstrumentSpa } from '../features/spa/instrument' import { Instrument as InstrumentSoftNav } from '../features/soft_navigations/instrument' -import { Instrument as InstrumentPageAction } from '../features/page_action/instrument' import { Instrument as InstrumentLogs } from '../features/logging/instrument' new Agent({ @@ -31,11 +29,9 @@ new Agent({ InstrumentSessionTrace, InstrumentSessionReplay, InstrumentMetrics, - InstrumentPageAction, InstrumentErrors, InstrumentGenericEvents, InstrumentLogs, - // InstrumentSpa, InstrumentSoftNav ], loaderType: 'experimental' diff --git a/src/cdn/polyfills/pro.js b/src/cdn/polyfills/pro.js index 22a9495a6..cc86f8ecb 100644 --- a/src/cdn/polyfills/pro.js +++ b/src/cdn/polyfills/pro.js @@ -12,7 +12,6 @@ import { Instrument as InstrumentMetrics } from '../../features/metrics/instrume import { Instrument as InstrumentErrors } from '../../features/jserrors/instrument' import { Instrument as InstrumentXhr } from '../../features/ajax/instrument' import { Instrument as InstrumentSessionTrace } from '../../features/session_trace/instrument' -import { Instrument as InstrumentPageAction } from '../../features/page_action/instrument' import { Instrument as InstrumentGenericEvents } from '../../features/generic_events/instrument' import { Instrument as InstrumentLogs } from '../../features/logging/instrument' @@ -23,7 +22,6 @@ new Agent({ InstrumentSessionTrace, InstrumentXhr, InstrumentMetrics, - InstrumentPageAction, InstrumentErrors, InstrumentGenericEvents, InstrumentLogs diff --git a/src/cdn/polyfills/spa.js b/src/cdn/polyfills/spa.js index 22edea03e..746a60ef6 100644 --- a/src/cdn/polyfills/spa.js +++ b/src/cdn/polyfills/spa.js @@ -13,7 +13,6 @@ import { Instrument as InstrumentErrors } from '../../features/jserrors/instrume import { Instrument as InstrumentXhr } from '../../features/ajax/instrument' import { Instrument as InstrumentSessionTrace } from '../../features/session_trace/instrument' import { Instrument as InstrumentSpa } from '../../features/spa/instrument' -import { Instrument as InstrumentPageAction } from '../../features/page_action/instrument' import { Instrument as InstrumentGenericEvents } from '../../features/generic_events/instrument' import { Instrument as InstrumentLogs } from '../../features/logging/instrument' @@ -24,7 +23,6 @@ new Agent({ InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentMetrics, - InstrumentPageAction, InstrumentErrors, InstrumentGenericEvents, InstrumentLogs, diff --git a/src/cdn/pro.js b/src/cdn/pro.js index 91a575267..50261b3f6 100644 --- a/src/cdn/pro.js +++ b/src/cdn/pro.js @@ -12,7 +12,6 @@ import { Instrument as InstrumentErrors } from '../features/jserrors/instrument' import { Instrument as InstrumentXhr } from '../features/ajax/instrument' import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument' import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument' -import { Instrument as InstrumentPageAction } from '../features/page_action/instrument' import { Instrument as InstrumentGenericEvents } from '../features/generic_events/instrument' import { Instrument as InstrumentLogs } from '../features/logging/instrument' @@ -24,7 +23,6 @@ new Agent({ InstrumentSessionReplay, InstrumentXhr, InstrumentMetrics, - InstrumentPageAction, InstrumentErrors, InstrumentGenericEvents, InstrumentLogs diff --git a/src/cdn/spa.js b/src/cdn/spa.js index 4bc9e91e3..96217dc4d 100644 --- a/src/cdn/spa.js +++ b/src/cdn/spa.js @@ -13,7 +13,6 @@ import { Instrument as InstrumentSessionTrace } from '../features/session_trace/ import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument' import { Instrument as InstrumentSoftNav } from '../features/soft_navigations/instrument' import { Instrument as InstrumentSpa } from '../features/spa/instrument' -import { Instrument as InstrumentPageAction } from '../features/page_action/instrument' import { Instrument as InstrumentGenericEvents } from '../features/generic_events/instrument' import { Instrument as InstrumentLogs } from '../features/logging/instrument' @@ -25,7 +24,6 @@ new Agent({ InstrumentSessionTrace, InstrumentSessionReplay, InstrumentMetrics, - InstrumentPageAction, InstrumentErrors, InstrumentGenericEvents, InstrumentLogs, diff --git a/src/common/config/state/init.js b/src/common/config/state/init.js index 8995faa86..469aaea85 100644 --- a/src/common/config/state/init.js +++ b/src/common/config/state/init.js @@ -41,13 +41,13 @@ const model = () => { allowed_origins: undefined }, feature_flags: [], - generic_events: { enabled: true, harvestTimeSeconds: 10, autoStart: true }, + generic_events: { enabled: true, harvestTimeSeconds: 30, autoStart: true }, harvest: { tooManyRequestsDelay: 60 }, jserrors: { enabled: true, harvestTimeSeconds: 10, autoStart: true }, logging: { enabled: true, harvestTimeSeconds: 10, autoStart: true, level: LOG_LEVELS.INFO }, metrics: { enabled: true, autoStart: true }, obfuscate: undefined, - page_action: { enabled: true, harvestTimeSeconds: 30, autoStart: true }, + page_action: { enabled: true }, page_view_event: { enabled: true, autoStart: true }, page_view_timing: { enabled: true, harvestTimeSeconds: 30, long_task: false, autoStart: true }, privacy: { cookies_enabled: true }, // *cli - per discussion, default should be true diff --git a/src/features/generic_events/aggregate/index.js b/src/features/generic_events/aggregate/index.js index 36a2b2ac5..771962e44 100644 --- a/src/features/generic_events/aggregate/index.js +++ b/src/features/generic_events/aggregate/index.js @@ -11,6 +11,7 @@ import { isBrowserScope } from '../../../common/constants/runtime' import { AggregateBase } from '../../utils/aggregate-base' import { warn } from '../../../common/util/console' import { now } from '../../../common/timing/now' +import { registerHandler } from '../../../common/event-emitter/register-handler' import { deregisterDrain } from '../../../common/drain/drain' export class Aggregate extends AggregateBase { @@ -22,7 +23,7 @@ export class Aggregate extends AggregateBase { this.eventsPerHarvest = 1000 this.harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'generic_events.harvestTimeSeconds') - this.referrerUrl = undefined + this.referrerUrl = (isBrowserScope && document.referrer) ? cleanURL(document.referrer) : undefined this.currentEvents = [] this.events = [] @@ -30,8 +31,6 @@ export class Aggregate extends AggregateBase { this.#agentRuntime = getRuntime(this.agentIdentifier) - if (isBrowserScope && document.referrer) this.referrerUrl = cleanURL(document.referrer) - this.waitForFlags(['ins']).then(([ins]) => { if (!ins) { this.blocked = true @@ -39,10 +38,27 @@ export class Aggregate extends AggregateBase { return } - // handle page actions and other generic events here + if (getConfigurationValue(this.agentIdentifier, 'page_action.enabled')) { + registerHandler('api-addPageAction', (timestamp, name, attributes) => { + this.addEvent({ + ...attributes, + eventType: 'PageAction', + timestamp: this.#agentRuntime.timeKeeper.convertRelativeTimestamp(timestamp), + timeSinceLoad: timestamp / 1000, + actionName: name, + referrerUrl: this.referrerUrl, + currentUrl: cleanURL('' + location), + ...(isBrowserScope && { + browserWidth: window.document.documentElement?.clientWidth, + browserHeight: window.document.documentElement?.clientHeight + }) + }) + }, this.featureName, this.ee) + } + this.harvestScheduler = new HarvestScheduler('ins', { onFinished: (...args) => this.onHarvestFinished(...args) }, this) this.harvestScheduler.harvest.on('ins', (...args) => this.onHarvestStarted(...args)) - // this.harvestScheduler.startTimer(this.harvestTimeSeconds, 0) + this.harvestScheduler.startTimer(this.harvestTimeSeconds, 0) this.drain() }) @@ -79,30 +95,31 @@ export class Aggregate extends AggregateBase { addEvent (obj = {}) { if (!obj || !Object.keys(obj).length) return if (!obj.eventType) { - warn('Invalid object passed to generic event aggregate. Missing "eventType".') + warn(44) return } for (let key in obj) { let val = obj[key] - if (key === 'timestamp') val = this.#agentRuntime.timeKeeper.correctAbsoluteTimestamp(val) obj[key] = (val && typeof val === 'object' ? stringify(val) : val) } + const defaultEventAttributes = { + /** should be overridden by the event-specific attributes, but just in case -- set it to now() */ + timestamp: this.#agentRuntime.timeKeeper.convertRelativeTimestamp(now()), + /** all generic events require a pageUrl */ + pageUrl: cleanURL(getRuntime(this.agentIdentifier).origin) + } + const eventAttributes = { /** Agent-level custom attributes */ ...(getInfo(this.agentIdentifier).jsAttributes || {}), - /** Common attributes shared on all generic events */ - referrerUrl: this.referrerUrl, - currentUrl: cleanURL('' + location), - pageUrl: cleanURL(getRuntime(this.agentIdentifier).origin), - /** Event-specific attributes take precedence over everything else */ + /** Fallbacks for required properties in-case the event did not supply them, should take precedence over agent-level custom attrs */ + ...defaultEventAttributes, + /** Event-specific attributes take precedence over agent-level custom attributes and fallbacks */ ...obj } - /** should have been provided by reporting feature -- but falls back to now if not */ - eventAttributes.timestamp ??= this.#agentRuntime.timeKeeper.convertRelativeTimestamp(now()) - this.events.push(eventAttributes) // check if we've reached the harvest limit... diff --git a/src/features/generic_events/instrument/index.js b/src/features/generic_events/instrument/index.js index 25acda845..5022278c4 100644 --- a/src/features/generic_events/instrument/index.js +++ b/src/features/generic_events/instrument/index.js @@ -2,6 +2,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { getConfigurationValue } from '../../../common/config/config' +import { deregisterDrain } from '../../../common/drain/drain' import { InstrumentBase } from '../../utils/instrument-base' import { FEATURE_NAME } from '../constants' @@ -9,6 +11,12 @@ export class Instrument extends InstrumentBase { static featureName = FEATURE_NAME constructor (agentIdentifier, aggregator, auto = true) { super(agentIdentifier, aggregator, FEATURE_NAME, auto) - this.importAggregator() + const genericEventSourceConfigs = [ + getConfigurationValue(this.agentIdentifier, 'page_action.enabled') + // other future generic event source configs to go here, like M&Ms, PageResouce, etc. + ] + /** If any of the sources are active, import the aggregator. otherwise deregister */ + if (genericEventSourceConfigs.some(x => x)) this.importAggregator() + else deregisterDrain(this.agentIdentifier, this.featureName) } } diff --git a/src/features/page_action/aggregate/index.js b/src/features/page_action/aggregate/index.js deleted file mode 100644 index a61fe458a..000000000 --- a/src/features/page_action/aggregate/index.js +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2020 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { stringify } from '../../../common/util/stringify' -import { registerHandler as register } from '../../../common/event-emitter/register-handler' -import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler' -import { cleanURL } from '../../../common/url/clean-url' -import { getConfigurationValue, getInfo, getRuntime } from '../../../common/config/config' -import { FEATURE_NAME } from '../constants' -import { isBrowserScope } from '../../../common/constants/runtime' -import { AggregateBase } from '../../utils/aggregate-base' -import { deregisterDrain } from '../../../common/drain/drain' - -export class Aggregate extends AggregateBase { - static featureName = FEATURE_NAME - constructor (agentIdentifier, aggregator) { - super(agentIdentifier, aggregator, FEATURE_NAME) - this.eventsPerMinute = 240 - this.harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'page_action.harvestTimeSeconds') || getConfigurationValue(this.agentIdentifier, 'ins.harvestTimeSeconds') || 30 - this.eventsPerHarvest = this.eventsPerMinute * this.harvestTimeSeconds / 60 - this.referrerUrl = undefined - this.currentEvents = undefined - - this.events = [] - - this.att = getInfo(this.agentIdentifier).jsAttributes // per-agent, aggregators-shared info context - - if (isBrowserScope && document.referrer) this.referrerUrl = cleanURL(document.referrer) - - register('api-addPageAction', (...args) => this.addPageAction(...args), this.featureName, this.ee) - - this.waitForFlags(['ins']).then(([insFlag]) => { - if (insFlag) { - const scheduler = new HarvestScheduler('ins', { onFinished: (...args) => this.onHarvestFinished(...args) }, this) - scheduler.harvest.on('ins', (...args) => this.onHarvestStarted(...args)) - scheduler.startTimer(this.harvestTimeSeconds, 0) - this.drain() - } else { - this.blocked = true // if rum response determines that customer lacks entitlements for ins endpoint, this feature shouldn't harvest - deregisterDrain(this.agentIdentifier, this.featureName) - } - }) - } - - onHarvestStarted (options) { - const { userAttributes, atts } = getInfo(this.agentIdentifier) - var payload = ({ - qs: { - ua: userAttributes, - at: atts - }, - body: { - ins: this.events - } - }) - - if (options.retry) { - this.currentEvents = this.events - } - - this.events = [] - return payload - } - - onHarvestFinished (result) { - if (result && result.sent && result.retry && this.currentEvents) { - this.events = this.events.concat(this.currentEvents) - this.currentEvents = null - } - } - - // WARNING: Insights times are in seconds. EXCEPT timestamp, which is in ms. - addPageAction (t, name, attributes) { - if (this.events.length >= this.eventsPerHarvest || this.blocked) return - var width - var height - var eventAttributes = {} - - if (isBrowserScope && window.document.documentElement) { - // Doesn't include the nav bar when it disappears in mobile safari - // https://github.com/jquery/jquery/blob/10399ddcf8a239acc27bdec9231b996b178224d3/src/dimensions.js#L23 - width = window.document.documentElement.clientWidth - height = window.document.documentElement.clientHeight - } - - const agentRuntime = getRuntime(this.agentIdentifier) - var defaults = { - timestamp: agentRuntime.timeKeeper.convertRelativeTimestamp(t), - timeSinceLoad: t / 1000, - browserWidth: width, - browserHeight: height, - referrerUrl: this.referrerUrl, - currentUrl: cleanURL('' + location), - pageUrl: cleanURL(agentRuntime.origin), - eventType: 'PageAction' - } - - Object.entries(defaults || {}).forEach(set) - Object.entries(getInfo(this.agentIdentifier).jsAttributes || {}).forEach(set) - if (attributes && typeof attributes === 'object') { - Object.entries(attributes).forEach(set) - } - eventAttributes.actionName = name || '' - - this.events.push(eventAttributes) - - function set ([key, val]) { - eventAttributes[key] = (val && typeof val === 'object' ? stringify(val) : val) - } - } -} diff --git a/src/features/page_action/instrument/index.js b/src/features/page_action/instrument/index.js index a784e4d3e..85664ccc7 100644 --- a/src/features/page_action/instrument/index.js +++ b/src/features/page_action/instrument/index.js @@ -3,13 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { InstrumentBase } from '../../utils/instrument-base' -import { FEATURE_NAME } from '../constants' +import { GenericEvents } from '../../generic_events' -export class Instrument extends InstrumentBase { - static featureName = FEATURE_NAME +/** + * @deprecated This feature has been replaced by Generic Events. Use/Import `GenericEvents` instead. This wrapper will be removed in a future release + */ +export class Instrument extends GenericEvents { constructor (agentIdentifier, aggregator, auto = true) { - super(agentIdentifier, aggregator, FEATURE_NAME, auto) - this.importAggregator() + super(agentIdentifier, aggregator, auto) } } diff --git a/src/features/page_view_event/aggregate/initialized-features.js b/src/features/page_view_event/aggregate/initialized-features.js index 087db039c..5d56d7aee 100644 --- a/src/features/page_view_event/aggregate/initialized-features.js +++ b/src/features/page_view_event/aggregate/initialized-features.js @@ -18,7 +18,7 @@ export function getActivatedFeaturesFlags (agentId) { flagArr.push('xhr'); break case FEATURE_NAMES.jserrors: flagArr.push('err'); break - case FEATURE_NAMES.pageAction: + case FEATURE_NAMES.genericEvents: flagArr.push('ins'); break case FEATURE_NAMES.sessionTrace: flagArr.push('stn'); break diff --git a/src/features/utils/lazy-feature-loader.js b/src/features/utils/lazy-feature-loader.js index d48b5073e..f995e9953 100644 --- a/src/features/utils/lazy-feature-loader.js +++ b/src/features/utils/lazy-feature-loader.js @@ -24,8 +24,6 @@ export function lazyFeatureLoader (featureName, featurePart) { return import(/* webpackChunkName: "logging-aggregate" */ '../logging/aggregate') case FEATURE_NAMES.metrics: return import(/* webpackChunkName: "metrics-aggregate" */ '../metrics/aggregate') - case FEATURE_NAMES.pageAction: - return import(/* webpackChunkName: "page_action-aggregate" */ '../page_action/aggregate') case FEATURE_NAMES.pageViewEvent: return import(/* webpackChunkName: "page_view_event-aggregate" */ '../page_view_event/aggregate') case FEATURE_NAMES.pageViewTiming: diff --git a/src/index.js b/src/index.js index ba6df7248..a08c6af5d 100644 --- a/src/index.js +++ b/src/index.js @@ -11,4 +11,5 @@ export { PageAction } from './features/page_action' export { PageViewEvent } from './features/page_view_event' export { PageViewTiming } from './features/page_view_timing' export { SessionTrace } from './features/session_trace' +export { SessionReplay } from './features/session_replay' export { Spa } from './features/spa' diff --git a/src/loaders/agent-base.js b/src/loaders/agent-base.js index 5c3dac987..0ebaae844 100644 --- a/src/loaders/agent-base.js +++ b/src/loaders/agent-base.js @@ -3,7 +3,6 @@ import { warn } from '../common/util/console' import { SR_EVENT_EMITTER_TYPES } from '../features/session_replay/constants' import { generateRandomHexString } from '../common/ids/unique-id' -import { ee } from '../common/event-emitter/contextual-ee' /** * @typedef {import('./api/interaction-types').InteractionInstance} InteractionInstance @@ -14,9 +13,6 @@ export class AgentBase { constructor (agentIdentifier = generateRandomHexString(16)) { this.agentIdentifier = agentIdentifier - - // Assign the observation context to the event emitter, so it knows how to create observation contexts - this.ee = ee.get(agentIdentifier) } /** diff --git a/src/loaders/api/api.js b/src/loaders/api/api.js index a28c7fc0a..7587d852c 100644 --- a/src/loaders/api/api.js +++ b/src/loaders/api/api.js @@ -69,7 +69,7 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false) // Setup stub functions that queue calls for later processing. asyncApiMethods.forEach(fnName => { apiInterface[fnName] = apiCall(prefix, fnName, true, 'api') }) - apiInterface.addPageAction = apiCall(prefix, 'addPageAction', true, FEATURE_NAMES.pageAction) + apiInterface.addPageAction = apiCall(prefix, 'addPageAction', true, FEATURE_NAMES.genericEvents) apiInterface.setPageViewName = function (name, host) { if (typeof name !== 'string') return diff --git a/src/loaders/api/apiAsync.js b/src/loaders/api/apiAsync.js index 698898ab5..fe39a85bd 100644 --- a/src/loaders/api/apiAsync.js +++ b/src/loaders/api/apiAsync.js @@ -27,7 +27,7 @@ export function setAPI (agentIdentifier) { var time = providedTime ? providedTime - originTime : t handle(CUSTOM_METRIC_CHANNEL, ['finished', { time }], undefined, FEATURE_NAMES.metrics, instanceEE) addToTrace(t, { name: 'finished', start: time + originTime, origin: 'nr' }) - handle('api-addPageAction', [time, 'finished'], undefined, FEATURE_NAMES.pageAction, instanceEE) + handle('api-addPageAction', [time, 'finished'], undefined, FEATURE_NAMES.genericEvents, instanceEE) } function addToTrace (t, evt) { diff --git a/src/loaders/browser-agent.js b/src/loaders/browser-agent.js index e658770b7..ed54115b7 100644 --- a/src/loaders/browser-agent.js +++ b/src/loaders/browser-agent.js @@ -7,7 +7,6 @@ import { Instrument as InstrumentErrors } from '../features/jserrors/instrument' import { Instrument as InstrumentXhr } from '../features/ajax/instrument' import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument' import { Instrument as InstrumentSpa } from '../features/spa/instrument' -import { Instrument as InstrumentPageAction } from '../features/page_action/instrument' import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument' import { Instrument as InstrumentGenericEvents } from '../features/generic_events/instrument' import { Instrument as InstrumentLogs } from '../features/logging/instrument' @@ -26,7 +25,6 @@ export class BrowserAgent extends Agent { InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentMetrics, - InstrumentPageAction, InstrumentErrors, InstrumentSpa, InstrumentSessionReplay, diff --git a/src/loaders/configure/configure.js b/src/loaders/configure/configure.js index fa95fc764..722b24d37 100644 --- a/src/loaders/configure/configure.js +++ b/src/loaders/configure/configure.js @@ -4,6 +4,7 @@ import { getConfiguration, setConfiguration, setInfo, setLoaderConfig, setRuntim import { activatedFeatures } from '../../common/util/feature-flags' import { isWorkerScope } from '../../common/constants/runtime' import { redefinePublicPath } from './public-path' +import { ee } from '../../common/event-emitter/contextual-ee' let alreadySetOnce = false // the configure() function can run multiple times in agent lifecycle @@ -12,7 +13,8 @@ let alreadySetOnce = false // the configure() function can run multiple times in */ export function configure (agent, opts = {}, loaderType, forceDrain) { // eslint-disable-next-line camelcase - let { init, info, loader_config, runtime = { loaderType }, exposed = true } = opts + let { init, info, loader_config, runtime = {}, exposed = true } = opts + runtime.loaderType = loaderType const nr = gosCDN() if (!info) { init = nr.init @@ -55,6 +57,8 @@ export function configure (agent, opts = {}, loaderType, forceDrain) { runtime.ptid = agent.agentIdentifier setRuntime(agent.agentIdentifier, runtime) + agent.ee = ee.get(agent.agentIdentifier) + if (agent.api === undefined) agent.api = setAPI(agent.agentIdentifier, forceDrain, agent.runSoftNavOverSpa) if (agent.exposed === undefined) agent.exposed = exposed alreadySetOnce = true diff --git a/src/loaders/features/features.js b/src/loaders/features/features.js index 85740342b..21e8a581e 100644 --- a/src/loaders/features/features.js +++ b/src/loaders/features/features.js @@ -4,6 +4,9 @@ export const FEATURE_NAMES = { jserrors: 'jserrors', logging: 'logging', metrics: 'metrics', + /** + * @deprecated This feature has been replaced by Generic Events. Use/Import `GenericEvents` instead. This wrapper will be removed in a future release + */ pageAction: 'page_action', pageViewEvent: 'page_view_event', pageViewTiming: 'page_view_timing', @@ -25,9 +28,8 @@ export const featurePriority = { [FEATURE_NAMES.spa]: 5, [FEATURE_NAMES.ajax]: 6, [FEATURE_NAMES.sessionTrace]: 7, - [FEATURE_NAMES.pageAction]: 8, - [FEATURE_NAMES.softNav]: 9, - [FEATURE_NAMES.sessionReplay]: 10, - [FEATURE_NAMES.logging]: 11, - [FEATURE_NAMES.genericEvents]: 12 + [FEATURE_NAMES.softNav]: 8, + [FEATURE_NAMES.sessionReplay]: 9, + [FEATURE_NAMES.logging]: 10, + [FEATURE_NAMES.genericEvents]: 11 } diff --git a/src/loaders/micro-agent.js b/src/loaders/micro-agent.js index 37dc57eed..1a2906e20 100644 --- a/src/loaders/micro-agent.js +++ b/src/loaders/micro-agent.js @@ -13,7 +13,7 @@ import { AgentBase } from './agent-base' const nonAutoFeatures = [ FEATURE_NAMES.jserrors, - FEATURE_NAMES.pageAction, + FEATURE_NAMES.genericEvents, FEATURE_NAMES.metrics ] diff --git a/tests/assets/instrumented-manual.html b/tests/assets/instrumented-manual.html index b906a173b..0a9f1bacf 100644 --- a/tests/assets/instrumented-manual.html +++ b/tests/assets/instrumented-manual.html @@ -11,7 +11,7 @@ NREUM.init.ajax.autoStart = false NREUM.init.jserrors.autoStart = false NREUM.init.metrics.autoStart = false - NREUM.init.page_action.autoStart = false + NREUM.init.generic_events.autoStart = false NREUM.init.page_view_event.autoStart = false NREUM.init.page_view_timing.autoStart = false NREUM.init.session_trace.autoStart = false diff --git a/tests/components/api.test.js b/tests/components/api.test.js index cea66b8bc..995421cbc 100644 --- a/tests/components/api.test.js +++ b/tests/components/api.test.js @@ -161,7 +161,7 @@ describe('setAPI', () => { 'api-addPageAction', [expect.toBeNumber(), ...args], null, - FEATURE_NAMES.pageAction, + FEATURE_NAMES.genericEvents, instanceEE ) }) diff --git a/tests/components/apiAsync.test.js b/tests/components/apiAsync.test.js index 5634ed807..74e9387db 100644 --- a/tests/components/apiAsync.test.js +++ b/tests/components/apiAsync.test.js @@ -99,7 +99,7 @@ describe('setAPI', () => { 'api-addPageAction', [time, 'finished'], undefined, - FEATURE_NAMES.pageAction, + FEATURE_NAMES.genericEvents, instanceEE ) }) @@ -128,7 +128,7 @@ describe('setAPI', () => { 'api-addPageAction', [providedTime - originTime, 'finished'], undefined, - FEATURE_NAMES.pageAction, + FEATURE_NAMES.genericEvents, instanceEE ) }) diff --git a/tests/components/contextual-ee.test.js b/tests/components/contextual-ee.test.js index af75e5e67..153328e0c 100644 --- a/tests/components/contextual-ee.test.js +++ b/tests/components/contextual-ee.test.js @@ -156,16 +156,16 @@ describe('event-emitter buffer', () => { expect(ee.backlog).toEqual({}) }) - test('it should not buffer after drain', async () => { + test('buffered data should not emit until drain', async () => { const { ee } = await import('../../src/common/event-emitter/contextual-ee') const { drain } = await import('../../src/common/drain/drain') + const { handle } = await import('../../src/common/event-emitter/handle') + const { registerHandler } = await import('../../src/common/event-emitter/register-handler') const mockListener = jest.fn() const eventType = faker.string.uuid() const eventArgs = ['a', 'b', 'c'] - ee.on(eventType, mockListener) - ee.buffer([eventType]) - ee.emit(eventType, eventArgs) + handle(eventType, eventArgs, undefined, undefined, ee) expect(ee.backlog).toEqual(expect.objectContaining({ feature: [ @@ -178,11 +178,13 @@ describe('event-emitter buffer', () => { ] })) expect(ee.isBuffering(eventType)).toEqual(true) + expect(mockListener).toHaveReturnedTimes(0) // wont return until drain is called + registerHandler(eventType, mockListener, undefined, ee) drain('globalEE') - ee.buffer([eventType]) - ee.emit(eventType, eventArgs) - ee.emit(eventType, eventArgs) + + handle(eventType, eventArgs, undefined, undefined, ee) + handle(eventType, eventArgs, undefined, undefined, ee) expect(ee.backlog).toEqual(expect.objectContaining({ feature: null diff --git a/tests/components/generic_events/aggregate/index.test.js b/tests/components/generic_events/aggregate/index.test.js new file mode 100644 index 000000000..e643458f7 --- /dev/null +++ b/tests/components/generic_events/aggregate/index.test.js @@ -0,0 +1,148 @@ +import { ee } from '../../../../src/common/event-emitter/contextual-ee' +import { Aggregator } from '../../../../src/common/aggregate/aggregator' +import { getConfiguration, getInfo, setInfo, setRuntime } from '../../../../src/common/config/config' +import { TimeKeeper } from '../../../../src/common/timing/time-keeper' +import { configure } from '../../../../src/loaders/configure/configure' + +const agentId = 'abcd' +const referrerUrl = 'https://test.com' +let genericEventsAgg, timeKeeper + +Object.defineProperty(global.document, 'referrer', { value: referrerUrl, configurable: true }) + +describe('Generic Events aggregate', () => { + beforeEach(async () => { + const agent = { agentIdentifier: agentId } + configure(agent, { + info: { licenseKey: 'licenseKey', applicationID: 'applicationID' }, + runtime: { isolatedBacklog: false }, + init: {} + }, 'test', true) + + timeKeeper = new TimeKeeper(agentId, ee.get(agentId)) + timeKeeper.processRumRequest({ + getResponseHeader: jest.fn(() => (new Date()).toUTCString()) + }, 450, 600) + setRuntime(agentId, { timeKeeper }) + + const { Aggregate } = await import('../../../../src/features/generic_events/aggregate') + + genericEventsAgg = new Aggregate(agentId, new Aggregator({ agentIdentifier: agentId, ee })) + }) + + it('should use default values', () => { + expect(genericEventsAgg).toMatchObject({ + eventsPerHarvest: 1000, + harvestTimeSeconds: 30, + referrerUrl: 'https://test.com', + currentEvents: [], + events: [], + overflow: [] + }) + }) + + it('should wait for flags - 1', async () => { + expect(genericEventsAgg.drained).not.toEqual(true) + genericEventsAgg.ee.emit('rumresp', [{ ins: 1 }]) + await wait(100) + expect(genericEventsAgg.drained).toEqual(true) + }) + + it('should wait for flags - 0', async () => { + expect(genericEventsAgg.drained).not.toEqual(true) + genericEventsAgg.ee.emit('rumresp', [{ ins: 0 }]) + await wait(100) + expect(genericEventsAgg.blocked).toEqual(true) + }) + + it('should warn if invalid event is provide', async () => { + console.debug = jest.fn() + genericEventsAgg.ee.emit('rumresp', [{ ins: 1 }]) + await wait(100) + genericEventsAgg.addEvent({ name: 'test' }) + expect(console.debug).toHaveBeenCalledWith('New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#44', undefined) + }) + + it('should only buffer 1000 events at a time', async () => { + genericEventsAgg.ee.emit('rumresp', [{ ins: 1 }]) + await wait(100) + genericEventsAgg.harvestScheduler.runHarvest = jest.fn() + for (let i = 1; i < 1000; i++) { + genericEventsAgg.addEvent({ name: i, eventType: 'test' }) + } + + expect(genericEventsAgg.harvestScheduler.runHarvest).not.toHaveBeenCalled() + genericEventsAgg.addEvent({ name: 1000, eventType: 'test' }) + expect(genericEventsAgg.harvestScheduler.runHarvest).toHaveBeenCalled() + expect(genericEventsAgg.events.length).toEqual(0) // 1000 - 1000 == 0 + for (let i = 1; i < 11; i++) { + genericEventsAgg.addEvent({ name: i, eventType: 'test' }) + } + expect(genericEventsAgg.events.length).toEqual(10) // 0 + 10 == 10 + }) + + describe('page_actions', () => { + beforeEach(async () => { + genericEventsAgg.ee.emit('rumresp', [{ ins: 1 }]) + await wait(100) + }) + + it('should record page actions if not disabled', () => { + const relativeTimestamp = Math.random() * 1000 + const name = 'name' + setInfo(agentId, { ...getInfo(agentId), jsAttributes: { globalFoo: 'globalBar' } }) + genericEventsAgg.ee.emit('api-addPageAction', [relativeTimestamp, name, { foo: 'bar' }]) + expect(genericEventsAgg.events[0]).toMatchObject({ + eventType: 'PageAction', + timestamp: timeKeeper.convertRelativeTimestamp(relativeTimestamp), + timeSinceLoad: relativeTimestamp / 1000, + actionName: name, + referrerUrl, + currentUrl: '' + location, + browserWidth: expect.any(Number), + browserHeight: expect.any(Number), + /** event custom attributes */ + foo: 'bar', + /** agent custom attributes */ + globalFoo: 'globalBar' + }) + }) + + it('event level custom attrs should not override protected attributes', () => { + const relativeTimestamp = Math.random() * 1000 + const name = 'name' + genericEventsAgg.ee.emit('api-addPageAction', [relativeTimestamp, name, { eventType: 'BetterPageAction', timestamp: 'BetterTimestamp' }]) + expect(genericEventsAgg.events[0]).toMatchObject({ + eventType: 'PageAction', + timestamp: expect.any(Number) + }) + }) + + it('agent level custom attrs should not override protected attributes', () => { + const relativeTimestamp = Math.random() * 1000 + const name = 'name' + setInfo(agentId, { ...getInfo(agentId), jsAttributes: { eventType: 'BetterPageAction', timestamp: 'BetterTimestamp' } }) + genericEventsAgg.ee.emit('api-addPageAction', [relativeTimestamp, name, {}]) + expect(genericEventsAgg.events[0]).toMatchObject({ + eventType: 'PageAction', + timestamp: expect.any(Number) + }) + }) + + it('should not record page actions if disabled', async () => { + const relativeTimestamp = Math.random() * 1000 + const name = 'name' + getConfiguration(agentId).page_action = { enabled: false } + const { Aggregate } = await import('../../../../src/features/generic_events/aggregate') + genericEventsAgg = new Aggregate(agentId, new Aggregator({ agentIdentifier: agentId, ee })) + genericEventsAgg.ee.emit('api-addPageAction', [relativeTimestamp, name, {}]) + expect(genericEventsAgg.events[0]).toBeUndefined() + }) + }) +}) + +function wait (ms) { + return new Promise((resolve, reject) => { + setTimeout(resolve, ms) + }) +} diff --git a/tests/components/generic_events/instrument/index.test.js b/tests/components/generic_events/instrument/index.test.js new file mode 100644 index 000000000..e457ffb85 --- /dev/null +++ b/tests/components/generic_events/instrument/index.test.js @@ -0,0 +1,51 @@ +import { TimeKeeper } from '../../../../src/common/timing/time-keeper' +import { configure } from '../../../../src/loaders/configure/configure' +import { setRuntime } from '../../../../src/common/config/config' +import { ee } from '../../../../src/common/event-emitter/contextual-ee' +import { Aggregator } from '../../../../src/common/aggregate/aggregator' + +let timeKeeper, genericEventsInst +const agentId = 'abcd' +describe('Generic Events instrument', () => { + beforeEach(async () => { + const agent = { agentIdentifier: agentId } + configure(agent, { + info: { licenseKey: 'licenseKey', applicationID: 'applicationID' }, + runtime: { isolatedBacklog: false }, + init: {} + }, 'test', true) + + timeKeeper = new TimeKeeper(agentId, ee.get(agentId)) + timeKeeper.processRumRequest({ + getResponseHeader: jest.fn(() => (new Date()).toUTCString()) + }, 450, 600) + setRuntime(agentId, { timeKeeper }) + + const { Instrument } = await import('../../../../src/features/generic_events/instrument') + + genericEventsInst = new Instrument(agentId, new Aggregator({ agentIdentifier: agentId, ee })) + }) + + it('should import if event source is enabled', async () => { + await wait(100) + expect(genericEventsInst.onAggregateImported).not.toBeUndefined() + }) + it('should not import if event source is not enabled', async () => { + const agent = { agentIdentifier: agentId } + configure(agent, { + info: { licenseKey: 'licenseKey', applicationID: 'applicationID' }, + runtime: { isolatedBacklog: false }, + init: { page_action: { enabled: false } } + }, 'test', true) + const { Instrument } = await import('../../../../src/features/generic_events/instrument') + genericEventsInst = new Instrument(agentId, new Aggregator({ agentIdentifier: agentId, ee })) + await wait(100) + expect(genericEventsInst.onAggregateImported).toBeUndefined() + }) +}) + +function wait (ms) { + return new Promise((resolve, reject) => { + setTimeout(resolve, ms) + }) +} diff --git a/tests/specs/cleanup-on-halt.e2e.js b/tests/specs/cleanup-on-halt.e2e.js index 18bb546a9..e079de03b 100644 --- a/tests/specs/cleanup-on-halt.e2e.js +++ b/tests/specs/cleanup-on-halt.e2e.js @@ -22,7 +22,7 @@ describe('Memory leaks', () => { ajax: 0, // ajax does not rely on any flags anyways so it's always drained jserrors: 0, metrics: 0, - page_action: 0, + generic_events: 0, page_view_event: 0, // no handler page_view_timing: 0, // does not rely on any flags session_trace: 0, diff --git a/tests/specs/ins/harvesting.e2e.js b/tests/specs/ins/harvesting.e2e.js index 062ff2cf2..7c33f499b 100644 --- a/tests/specs/ins/harvesting.e2e.js +++ b/tests/specs/ins/harvesting.e2e.js @@ -35,7 +35,9 @@ describe('ins harvesting', () => { browser.testHandle.expectIns(), browser.execute(function () { newrelic.setCustomAttribute('browserHeight', 705) - newrelic.addPageAction('MyEvent', { referrerUrl: 'http://test.com', foo: { bar: 'baz' } }) + newrelic.setCustomAttribute('eventType', 'globalPageAction') + newrelic.setCustomAttribute('globalCustomAttribute', 12345) + newrelic.addPageAction('MyEvent', { referrerUrl: 'http://test.com', localCustomAttribute: { bar: 'baz' }, eventType: 'localPageAction' }) }) ]) @@ -43,10 +45,10 @@ describe('ins harvesting', () => { let event = pageActionsHarvest[0] expect(event.actionName).toEqual('MyEvent') - expect(event.eventType).toEqual('PageAction') //, 'defaults has correct precedence') - expect(event.browserHeight).toEqual(705) //, 'att has correct precedence') - expect(event.referrerUrl).toEqual('http://test.com') //, 'attributes has correct precedence') - expect(event.foo).toEqual('{"bar":"baz"}') //, 'custom member of attributes passed through') + expect(event.eventType).toEqual('PageAction') //, 'pageAction should not be overwritable (globalPageAction, localPageAction) + expect(event.browserHeight).not.toEqual(705) //, 'browser height should not be overwritable' + expect(event.globalCustomAttribute).toEqual(12345) //, 'global custom attributes passed through') + expect(event.localCustomAttribute).toEqual('{"bar":"baz"}') //, 'local custom attributes passed through') }) it('NEWRELIC-9370: should not throw an exception when calling addPageAction with window.location before navigating', async () => { diff --git a/tests/specs/npm/index.e2e.js b/tests/specs/npm/index.e2e.js index 68b100715..c4a304c6a 100644 --- a/tests/specs/npm/index.e2e.js +++ b/tests/specs/npm/index.e2e.js @@ -6,7 +6,8 @@ const testBuilds = [ 'custom-agent-lite', 'custom-agent-pro', 'custom-agent-spa', - 'worker-agent' + 'worker-agent', + 'custom-agent-pro-deprecated-features' ] describe.withBrowsersMatching(es2022Support)('basic npm agent', () => { diff --git a/tests/unit/common/config/state/init.test.js b/tests/unit/common/config/state/init.test.js index 7ba0bdcca..d3cc65c52 100644 --- a/tests/unit/common/config/state/init.test.js +++ b/tests/unit/common/config/state/init.test.js @@ -19,15 +19,15 @@ test('set/getConfiguration works correctly', () => { let cachedObj = getConfiguration('123') expect(Object.keys(cachedObj).length).toBeGreaterThan(1) expect(cachedObj.jserrors.enabled).toEqual(false) - expect(cachedObj.page_action.enabled).toEqual(true) // this should mirror default in init.js + expect(cachedObj.generic_events.enabled).toEqual(true) // this should mirror default in init.js }) test('getConfigurationValue parses path correctly', () => { - setConfiguration('ab', { page_action: { harvestTimeSeconds: 1000 } }) + setConfiguration('ab', { generic_events: { harvestTimeSeconds: 1000 } }) expect(getConfigurationValue('ab', '')).toBeUndefined() - expect(getConfigurationValue('ab', 'page_action')).toEqual({ enabled: true, harvestTimeSeconds: 1000, autoStart: true }) - expect(getConfigurationValue('ab', 'page_action.harvestTimeSeconds')).toEqual(1000) - expect(getConfigurationValue('ab', 'page_action.dne')).toBeUndefined() + expect(getConfigurationValue('ab', 'generic_events')).toEqual({ enabled: true, harvestTimeSeconds: 1000, autoStart: true }) + expect(getConfigurationValue('ab', 'generic_events.harvestTimeSeconds')).toEqual(1000) + expect(getConfigurationValue('ab', 'generic_events.dne')).toBeUndefined() }) test('init props exist and return expected defaults', () => { @@ -51,7 +51,7 @@ test('init props exist and return expected defaults', () => { expect(config.generic_events).toEqual({ autoStart: true, enabled: true, - harvestTimeSeconds: 10 + harvestTimeSeconds: 30 }) expect(config.feature_flags).toEqual([]) expect(config.harvest).toEqual({ @@ -74,9 +74,7 @@ test('init props exist and return expected defaults', () => { }) expect(config.obfuscate).toEqual(undefined) expect(config.page_action).toEqual({ - autoStart: true, - enabled: true, - harvestTimeSeconds: 30 + enabled: true }) expect(config.page_view_event).toEqual({ autoStart: true, diff --git a/tests/unit/features/utils/lazy-feature-loader.test.js b/tests/unit/features/utils/lazy-feature-loader.test.js index 71383d948..8c00673c1 100644 --- a/tests/unit/features/utils/lazy-feature-loader.test.js +++ b/tests/unit/features/utils/lazy-feature-loader.test.js @@ -8,9 +8,16 @@ jest.enableAutomock() jest.unmock('../../../../src/loaders/features/features') jest.unmock('../../../../src/features/utils/lazy-feature-loader') +const randomIds = {} +const featureRedirects = { + [FEATURE_NAMES.pageAction]: FEATURE_NAMES.genericEvents +} + test.each(Object.keys(FEATURE_NAMES))('should import the aggregate for feature %s', async (key) => { - const featureName = FEATURE_NAMES[key] - const randomId = faker.string.uuid() + const featureName = featureRedirects[FEATURE_NAMES[key]] || FEATURE_NAMES[key] // deprecated page_actions redirects to use generic events, otherwise use provided key + /** since both generic events and page actions now use the same mocked agg, the randomId should be buffered/referenced here instead of overwriting */ + const randomId = randomIds[featureName] || faker.string.uuid() + randomIds[featureName] ??= randomId jest.setMock(`../../../../src/features/${featureName}/aggregate`, { id: randomId, diff --git a/tests/unit/loaders/browser-agent.test.js b/tests/unit/loaders/browser-agent.test.js index 2fd00737f..1d0cd6df5 100644 --- a/tests/unit/loaders/browser-agent.test.js +++ b/tests/unit/loaders/browser-agent.test.js @@ -7,7 +7,6 @@ import { Instrument as InstrumentErrors } from '../../../src/features/jserrors/i import { Instrument as InstrumentXhr } from '../../../src/features/ajax/instrument' import { Instrument as InstrumentSessionTrace } from '../../../src/features/session_trace/instrument' import { Instrument as InstrumentSpa } from '../../../src/features/spa/instrument' -import { Instrument as InstrumentPageAction } from '../../../src/features/page_action/instrument' import { Instrument as InstrumentSessionReplay } from '../../../src/features/session_replay/instrument' import { Instrument as InstrumentGenericEvents } from '../../../src/features/generic_events/instrument' import { Instrument as InstrumentLogs } from '../../../src/features/logging/instrument' @@ -26,7 +25,6 @@ test('should create a new agent with all features', () => { InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentMetrics, - InstrumentPageAction, InstrumentErrors, InstrumentSpa, InstrumentSessionReplay, diff --git a/tools/test-builds/browser-agent-wrapper/src/custom-agent-pro-deprecated-features.js b/tools/test-builds/browser-agent-wrapper/src/custom-agent-pro-deprecated-features.js new file mode 100644 index 000000000..86db320d8 --- /dev/null +++ b/tools/test-builds/browser-agent-wrapper/src/custom-agent-pro-deprecated-features.js @@ -0,0 +1,23 @@ +import { Agent } from '@newrelic/browser-agent/loaders/agent' +import { Ajax } from '@newrelic/browser-agent/features/ajax' +import { PageAction } from '@newrelic/browser-agent/features/page_action' +import { JSErrors } from '@newrelic/browser-agent/features/jserrors' +import { Metrics } from '@newrelic/browser-agent/features/metrics' +import { PageViewEvent } from '@newrelic/browser-agent/features/page_view_event' +import { PageViewTiming } from '@newrelic/browser-agent/features/page_view_timing' +import { SessionTrace } from '@newrelic/browser-agent/features/session_trace' +import { SessionReplay } from '@newrelic/browser-agent/features/session_replay' + +window.agent = new Agent({ + features: [ + Ajax, + PageAction, // PageAction is now deprecated in favor of GenericEvents, BUT the loader should still work if this is the entry point + JSErrors, + Metrics, + PageViewEvent, + PageViewTiming, + SessionTrace, + SessionReplay + ], + loaderType: 'pro' +}) diff --git a/tools/test-builds/browser-agent-wrapper/src/custom-agent-pro.js b/tools/test-builds/browser-agent-wrapper/src/custom-agent-pro.js index 7b46bfce6..95a1f7dee 100644 --- a/tools/test-builds/browser-agent-wrapper/src/custom-agent-pro.js +++ b/tools/test-builds/browser-agent-wrapper/src/custom-agent-pro.js @@ -3,7 +3,6 @@ import { Ajax } from '@newrelic/browser-agent/features/ajax' import { GenericEvents } from '@newrelic/browser-agent/features/generic_events' import { JSErrors } from '@newrelic/browser-agent/features/jserrors' import { Metrics } from '@newrelic/browser-agent/features/metrics' -import { PageAction } from '@newrelic/browser-agent/features/page_action' import { PageViewEvent } from '@newrelic/browser-agent/features/page_view_event' import { PageViewTiming } from '@newrelic/browser-agent/features/page_view_timing' import { SessionTrace } from '@newrelic/browser-agent/features/session_trace' @@ -15,7 +14,6 @@ window.agent = new Agent({ GenericEvents, JSErrors, Metrics, - PageAction, PageViewEvent, PageViewTiming, SessionTrace, diff --git a/tools/test-builds/browser-agent-wrapper/src/custom-agent-spa.js b/tools/test-builds/browser-agent-wrapper/src/custom-agent-spa.js index 5ff18ff13..b7f54b00f 100644 --- a/tools/test-builds/browser-agent-wrapper/src/custom-agent-spa.js +++ b/tools/test-builds/browser-agent-wrapper/src/custom-agent-spa.js @@ -3,7 +3,6 @@ import { Ajax } from '@newrelic/browser-agent/features/ajax' import { GenericEvents } from '@newrelic/browser-agent/features/generic_events' import { JSErrors } from '@newrelic/browser-agent/features/jserrors' import { Metrics } from '@newrelic/browser-agent/features/metrics' -import { PageAction } from '@newrelic/browser-agent/features/page_action' import { PageViewEvent } from '@newrelic/browser-agent/features/page_view_event' import { PageViewTiming } from '@newrelic/browser-agent/features/page_view_timing' import { SessionTrace } from '@newrelic/browser-agent/features/session_trace' @@ -17,7 +16,6 @@ window.agent = new Agent({ GenericEvents, JSErrors, Metrics, - PageAction, PageViewEvent, PageViewTiming, SessionTrace, diff --git a/tools/test-builds/browser-agent-wrapper/src/worker-agent.js b/tools/test-builds/browser-agent-wrapper/src/worker-agent.js index 435da02ba..966604d60 100644 --- a/tools/test-builds/browser-agent-wrapper/src/worker-agent.js +++ b/tools/test-builds/browser-agent-wrapper/src/worker-agent.js @@ -2,7 +2,7 @@ import { Agent } from '@newrelic/browser-agent/loaders/agent' import { Metrics } from '@newrelic/browser-agent/features/metrics' import { JSErrors } from '@newrelic/browser-agent/features/jserrors' import { Ajax } from '@newrelic/browser-agent/features/ajax' -import { PageAction } from '@newrelic/browser-agent/features/page_action' +import { GenericEvents } from '@newrelic/browser-agent/features/generic_events' export function workerAgentFactory (opts) { return new Agent({ @@ -11,7 +11,7 @@ export function workerAgentFactory (opts) { Metrics, JSErrors, Ajax, - PageAction + GenericEvents ], loaderType: 'worker' }) diff --git a/tools/test-builds/browser-agent-wrapper/webpack.config.js b/tools/test-builds/browser-agent-wrapper/webpack.config.js index 79faaa107..e332fd1d5 100644 --- a/tools/test-builds/browser-agent-wrapper/webpack.config.js +++ b/tools/test-builds/browser-agent-wrapper/webpack.config.js @@ -34,6 +34,7 @@ const config = [ 'browser-agent': './src/browser-agent.js', 'custom-agent-lite': './src/custom-agent-lite.js', 'custom-agent-pro': './src/custom-agent-pro.js', + 'custom-agent-pro-deprecated-features': './src/custom-agent-pro-deprecated-features.js', 'custom-agent-spa': './src/custom-agent-spa.js', 'micro-agent': './src/micro-agent.js', // worker init script @@ -97,6 +98,12 @@ const config = [ inject: false, templateContent: htmlTemplate('custom-agent-pro') }), + new HtmlWebpackPlugin({ + filename: 'custom-agent-pro-deprecated-features.html', + minify: false, + inject: false, + templateContent: htmlTemplate('custom-agent-pro-deprecated-features') + }), new HtmlWebpackPlugin({ filename: 'custom-agent-spa.html', minify: false, diff --git a/tools/test-builds/raw-src-wrapper/src/custom-agent-pro-deprecated-features.js b/tools/test-builds/raw-src-wrapper/src/custom-agent-pro-deprecated-features.js new file mode 100644 index 000000000..a6df6a6ec --- /dev/null +++ b/tools/test-builds/raw-src-wrapper/src/custom-agent-pro-deprecated-features.js @@ -0,0 +1,23 @@ +import { Agent } from '@newrelic/browser-agent/src/loaders/agent' +import { Ajax } from '@newrelic/browser-agent/src/features/ajax' +import { PageAction } from '@newrelic/browser-agent/src/features/page_action' +import { JSErrors } from '@newrelic/browser-agent/src/features/jserrors' +import { Metrics } from '@newrelic/browser-agent/src/features/metrics' +import { PageViewEvent } from '@newrelic/browser-agent/src/features/page_view_event' +import { PageViewTiming } from '@newrelic/browser-agent/src/features/page_view_timing' +import { SessionTrace } from '@newrelic/browser-agent/src/features/session_trace' +import { SessionReplay } from '@newrelic/browser-agent/src/features/session_replay' + +window.agent = new Agent({ + features: [ + Ajax, + PageAction, // PageAction is now deprecated in favor of GenericEvents, BUT the loader should still work if this is the entry point + JSErrors, + Metrics, + PageViewEvent, + PageViewTiming, + SessionTrace, + SessionReplay + ], + loaderType: 'pro' +}) diff --git a/tools/test-builds/raw-src-wrapper/src/custom-agent-pro.js b/tools/test-builds/raw-src-wrapper/src/custom-agent-pro.js index d925a452f..b901bb75a 100644 --- a/tools/test-builds/raw-src-wrapper/src/custom-agent-pro.js +++ b/tools/test-builds/raw-src-wrapper/src/custom-agent-pro.js @@ -3,7 +3,6 @@ import { Ajax } from '@newrelic/browser-agent/src/features/ajax' import { GenericEvents } from '@newrelic/browser-agent/src/features/generic_events' import { JSErrors } from '@newrelic/browser-agent/src/features/jserrors' import { Metrics } from '@newrelic/browser-agent/src/features/metrics' -import { PageAction } from '@newrelic/browser-agent/src/features/page_action' import { PageViewEvent } from '@newrelic/browser-agent/src/features/page_view_event' import { PageViewTiming } from '@newrelic/browser-agent/src/features/page_view_timing' import { SessionTrace } from '@newrelic/browser-agent/src/features/session_trace' @@ -15,7 +14,6 @@ window.agent = new Agent({ GenericEvents, JSErrors, Metrics, - PageAction, PageViewEvent, PageViewTiming, SessionTrace, diff --git a/tools/test-builds/raw-src-wrapper/src/custom-agent-spa.js b/tools/test-builds/raw-src-wrapper/src/custom-agent-spa.js index 4a21f7eb0..b393a983a 100644 --- a/tools/test-builds/raw-src-wrapper/src/custom-agent-spa.js +++ b/tools/test-builds/raw-src-wrapper/src/custom-agent-spa.js @@ -3,7 +3,6 @@ import { Ajax } from '@newrelic/browser-agent/src/features/ajax' import { GenericEvents } from '@newrelic/browser-agent/src/features/generic_events' import { JSErrors } from '@newrelic/browser-agent/src/features/jserrors' import { Metrics } from '@newrelic/browser-agent/src/features/metrics' -import { PageAction } from '@newrelic/browser-agent/src/features/page_action' import { PageViewEvent } from '@newrelic/browser-agent/src/features/page_view_event' import { PageViewTiming } from '@newrelic/browser-agent/src/features/page_view_timing' import { SessionTrace } from '@newrelic/browser-agent/src/features/session_trace' @@ -17,7 +16,6 @@ window.agent = new Agent({ GenericEvents, JSErrors, Metrics, - PageAction, PageViewEvent, PageViewTiming, SessionTrace, diff --git a/tools/test-builds/raw-src-wrapper/src/worker-agent.js b/tools/test-builds/raw-src-wrapper/src/worker-agent.js index beef02a0d..58665079f 100644 --- a/tools/test-builds/raw-src-wrapper/src/worker-agent.js +++ b/tools/test-builds/raw-src-wrapper/src/worker-agent.js @@ -2,7 +2,7 @@ import { Agent } from '@newrelic/browser-agent/src/loaders/agent' import { Metrics } from '@newrelic/browser-agent/src/features/metrics' import { JSErrors } from '@newrelic/browser-agent/src/features/jserrors' import { Ajax } from '@newrelic/browser-agent/src/features/ajax' -import { PageAction } from '@newrelic/browser-agent/src/features/page_action' +import { GenericEvents } from '@newrelic/browser-agent/src/features/generic_events' export function workerAgentFactory (opts) { return new Agent({ @@ -11,7 +11,7 @@ export function workerAgentFactory (opts) { Metrics, JSErrors, Ajax, - PageAction + GenericEvents ], loaderType: 'worker' }) diff --git a/tools/test-builds/raw-src-wrapper/webpack.config.js b/tools/test-builds/raw-src-wrapper/webpack.config.js index 9c65f2a82..7ed3298a1 100644 --- a/tools/test-builds/raw-src-wrapper/webpack.config.js +++ b/tools/test-builds/raw-src-wrapper/webpack.config.js @@ -34,6 +34,7 @@ const config = [ 'browser-agent': './src/browser-agent.js', 'custom-agent-lite': './src/custom-agent-lite.js', 'custom-agent-pro': './src/custom-agent-pro.js', + 'custom-agent-pro-deprecated-features': './src/custom-agent-pro-deprecated-features.js', 'custom-agent-spa': './src/custom-agent-spa.js', 'micro-agent': './src/micro-agent.js', // worker init script @@ -91,6 +92,12 @@ const config = [ inject: false, templateContent: htmlTemplate('custom-agent-pro') }), + new HtmlWebpackPlugin({ + filename: 'custom-agent-pro-deprecated-features.html', + minify: false, + inject: false, + templateContent: htmlTemplate('custom-agent-pro-deprecated-features') + }), new HtmlWebpackPlugin({ filename: 'custom-agent-spa.html', minify: false, diff --git a/tools/test-builds/vite-react-wrapper/types-resolver/custom-agent.ts b/tools/test-builds/vite-react-wrapper/types-resolver/custom-agent.ts index 69d67ec1f..3bb4b5b8f 100644 --- a/tools/test-builds/vite-react-wrapper/types-resolver/custom-agent.ts +++ b/tools/test-builds/vite-react-wrapper/types-resolver/custom-agent.ts @@ -2,7 +2,7 @@ import { Agent } from "@newrelic/browser-agent/loaders/agent" import { Ajax } from '@newrelic/browser-agent/features/ajax' import { JSErrors } from '@newrelic/browser-agent/features/jserrors' import { Metrics } from '@newrelic/browser-agent/features/metrics' -import { PageAction } from '@newrelic/browser-agent/features/page_action' +import { GenericEvents } from '@newrelic/browser-agent/features/generic_events' import { PageViewEvent } from '@newrelic/browser-agent/features/page_view_event' import { PageViewTiming } from '@newrelic/browser-agent/features/page_view_timing' import { SessionTrace } from '@newrelic/browser-agent/features/session_trace' diff --git a/tools/testing-server/constants.js b/tools/testing-server/constants.js index 07014e2a9..82ba0fa90 100644 --- a/tools/testing-server/constants.js +++ b/tools/testing-server/constants.js @@ -58,7 +58,7 @@ module.exports.defaultInitBlock = { logging: enabledFeature, metrics: { enabled, autoStart }, obfuscate: undefined, - page_action: enabledFeature, + page_action: { enabled }, page_view_event: { enabled, autoStart }, page_view_timing: enabledFeature, privacy: { cookies_enabled: true },