@atlaskit/react-ufo 5.0.13 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @atlaskit/ufo-interaction-ignore
2
2
 
3
+ ## 5.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`c4ed6da74c937`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c4ed6da74c937) -
8
+ OBSRVE-2971 Adding the OTel Context Manager to handle trace context for React UFO tracing
9
+
10
+ ### Patch Changes
11
+
12
+ - [`cd27ffb264a01`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/cd27ffb264a01) -
13
+ Added config option for sending the terminal error metric
14
+ - [`ac82093b2419b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/ac82093b2419b) -
15
+ bugfixes with disabling TTVC v3
16
+
3
17
  ## 5.0.13
4
18
 
5
19
  ### Patch Changes
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.UFOContextManager = void 0;
8
+ exports.getContextManager = getContextManager;
9
+ exports.setContextManager = setContextManager;
10
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
11
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
12
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
13
+ var _api = require("@opentelemetry/api");
14
+ /**
15
+ * We need to store a reference to the Context manager so that we can get it later.
16
+ * This is because the OTel JS API doesn't allow us to get the registered context manager,
17
+ * and we need to get it because we need to call a function that's not available in the OTel API:
18
+ * `setActive`. The OTel JS API design doesn't allow us to manually set the active context
19
+ * despite the spec allowing for the capability. Sigh.
20
+ *
21
+ * I imagine this situation might not be permanent if we can move to a system were we can rely
22
+ * solely on the API, but for the purposes of the first change being React UFO API compatible,
23
+ * we'll need to do this.
24
+ */
25
+
26
+ var contextManager;
27
+ function setContextManager(ctxMgr) {
28
+ contextManager = ctxMgr;
29
+ }
30
+ function getContextManager() {
31
+ return contextManager;
32
+ }
33
+
34
+ /**
35
+ * The below is shamelessly borrowed from StackContextManager from @opentelemetry/sdk-trace-web
36
+ * Using this rather than importing the entire package because this is all we need for now
37
+ */
38
+
39
+ /**
40
+ * UFO Context Manager for managing the state in web
41
+ * it doesn't fully support the async calls though
42
+ */
43
+ var UFOContextManager = exports.UFOContextManager = /*#__PURE__*/function () {
44
+ function UFOContextManager() {
45
+ (0, _classCallCheck2.default)(this, UFOContextManager);
46
+ /**
47
+ * whether the context manager is enabled or not
48
+ */
49
+ (0, _defineProperty2.default)(this, "_enabled", false);
50
+ /**
51
+ * Keeps the reference to current context
52
+ */
53
+ (0, _defineProperty2.default)(this, "_currentContext", _api.ROOT_CONTEXT);
54
+ }
55
+ return (0, _createClass2.default)(UFOContextManager, [{
56
+ key: "_bindFunction",
57
+ value:
58
+ /**
59
+ *
60
+ * @param context
61
+ * @param target Function to be executed within the context
62
+ */
63
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
64
+ function _bindFunction() {
65
+ var context = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _api.ROOT_CONTEXT;
66
+ var target = arguments.length > 1 ? arguments[1] : undefined;
67
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
68
+ var manager = this;
69
+ var contextWrapper = function contextWrapper() {
70
+ var _this = this;
71
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
72
+ args[_key] = arguments[_key];
73
+ }
74
+ return manager.with(context, function () {
75
+ return target.apply(_this, args);
76
+ });
77
+ };
78
+ Object.defineProperty(contextWrapper, 'length', {
79
+ enumerable: false,
80
+ configurable: true,
81
+ writable: false,
82
+ value: target.length
83
+ });
84
+ return contextWrapper;
85
+ }
86
+
87
+ /**
88
+ * Returns the active context
89
+ */
90
+ }, {
91
+ key: "active",
92
+ value: function active() {
93
+ return this._currentContext;
94
+ }
95
+
96
+ /**
97
+ * Binds a the certain context or the active one to the target function and then returns the target
98
+ * @param context A context (span) to be bind to target
99
+ * @param target a function or event emitter. When target or one of its callbacks is called,
100
+ * the provided context will be used as the active context for the duration of the call.
101
+ */
102
+ }, {
103
+ key: "bind",
104
+ value: function bind(context, target) {
105
+ // if no specific context to propagate is given, we use the current one
106
+ if (context === undefined) {
107
+ context = this.active();
108
+ }
109
+ if (typeof target === 'function') {
110
+ return this._bindFunction(context, target);
111
+ }
112
+ return target;
113
+ }
114
+
115
+ /**
116
+ * Disable the context manager (clears the current context)
117
+ */
118
+ }, {
119
+ key: "disable",
120
+ value: function disable() {
121
+ this._currentContext = _api.ROOT_CONTEXT;
122
+ this._enabled = false;
123
+ return this;
124
+ }
125
+
126
+ /**
127
+ * Enables the context manager and creates a default(root) context
128
+ */
129
+ }, {
130
+ key: "enable",
131
+ value: function enable() {
132
+ if (this._enabled) {
133
+ return this;
134
+ }
135
+ this._enabled = true;
136
+ this._currentContext = _api.ROOT_CONTEXT;
137
+ return this;
138
+ }
139
+
140
+ /**
141
+ * Calls the callback function [fn] with the provided [context]. If [context] is undefined then it will use the window.
142
+ * The context will be set as active
143
+ * @param context
144
+ * @param fn Callback function
145
+ * @param thisArg optional receiver to be used for calling fn
146
+ * @param args optional arguments forwarded to fn
147
+ */
148
+ }, {
149
+ key: "with",
150
+ value: function _with(context, fn, thisArg) {
151
+ var previousContext = this._currentContext;
152
+ this._currentContext = context || _api.ROOT_CONTEXT;
153
+ try {
154
+ for (var _len2 = arguments.length, args = new Array(_len2 > 3 ? _len2 - 3 : 0), _key2 = 3; _key2 < _len2; _key2++) {
155
+ args[_key2 - 3] = arguments[_key2];
156
+ }
157
+ return fn.call.apply(fn, [thisArg].concat(args));
158
+ } finally {
159
+ this._currentContext = previousContext;
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Sets the active context.
165
+ * This function is an extension of the OTel spec, in order to facilitate the current API of React UFO trace context handling.
166
+ * It doesn't keep track of any previously set active contexts, because it's assumed (for now) that only one trace context
167
+ * will ever exist at a time.
168
+ * The next step in the work to improve tracing in the FE will likely remove this function as we need to track the
169
+ * hierarchy of contexts (likely using the `with()` function above).
170
+ * @param context The context to be made the globally active one
171
+ */
172
+ }, {
173
+ key: "setActive",
174
+ value: function setActive(context) {
175
+ if (this._enabled) {
176
+ this._currentContext = context;
177
+ }
178
+ }
179
+ }]);
180
+ }();
@@ -10,38 +10,94 @@ exports.getActiveTraceAsQueryParams = getActiveTraceAsQueryParams;
10
10
  exports.getActiveTraceHttpRequestHeaders = getActiveTraceHttpRequestHeaders;
11
11
  exports.setActiveTrace = setActiveTrace;
12
12
  exports.setInteractionActiveTrace = setInteractionActiveTrace;
13
+ var _api = require("@opentelemetry/api");
14
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
15
+ var _contextManager = require("./context-manager");
13
16
  var _makeTraceHttpRequestHeaders = require("./utils/make-trace-http-request-headers");
14
17
  var state = {
15
18
  context: null
16
19
  };
20
+ var traceIdKey = (0, _api.createContextKey)("traceId");
21
+ var spanIdKey = (0, _api.createContextKey)("spanId");
22
+ var experienceTypeKey = (0, _api.createContextKey)("type");
23
+
24
+ // DO NOT CALL THIS FUNCTION DIRECTLY!!!!
25
+ // It is only to be called by React UFO libraries for the automatic handling of trace context for experiences.
26
+ // Calling this may cause trace context to be broken
17
27
  function generateSpanId() {
18
28
  return Array.from(new Array(16), function () {
19
29
  return Math.floor(Math.random() * 16).toString(16);
20
30
  }).join('');
21
31
  }
32
+
33
+ // DO NOT CALL THIS FUNCTION DIRECTLY!!!!
34
+ // It is only to be called by React UFO libraries for the automatic handling of trace context for experiences.
35
+ // Calling this may cause trace context to be broken
22
36
  function setInteractionActiveTrace(interactionId, experienceType) {
23
37
  setActiveTrace(interactionId.replace(/-/g, ''), generateSpanId(), experienceType);
24
38
  }
39
+
40
+ // DO NOT CALL THIS FUNCTION DIRECTLY!!!!
41
+ // It is only to be called by React UFO libraries for the automatic handling of trace context for experiences.
42
+ // Calling this may cause trace context to be broken
25
43
  function setActiveTrace(traceId, spanId, type) {
26
- state.context = {
27
- traceId: traceId,
28
- spanId: spanId,
29
- type: type
30
- };
44
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_enable_otel_context_manager')) {
45
+ var activeTraceContext = _api.ROOT_CONTEXT.setValue(traceIdKey, traceId).setValue(spanIdKey, spanId).setValue(experienceTypeKey, type);
46
+
47
+ // Now we need to get the global Context Manager and set the active context
48
+ // Using type assertion because we've "extended" the ContextManager type
49
+ if ((0, _contextManager.getContextManager)() instanceof _contextManager.UFOContextManager) {
50
+ var contextManager = (0, _contextManager.getContextManager)();
51
+ contextManager.setActive(activeTraceContext);
52
+ }
53
+ } else {
54
+ state.context = {
55
+ traceId: traceId,
56
+ spanId: spanId,
57
+ type: type
58
+ };
59
+ }
31
60
  }
32
61
  function getActiveTrace() {
33
- return state.context || undefined;
62
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_enable_otel_context_manager')) {
63
+ // Get trace context from active context
64
+ var activeTraceContext = {
65
+ traceId: String(_api.context.active().getValue(traceIdKey)),
66
+ spanId: String(_api.context.active().getValue(spanIdKey)),
67
+ type: String(_api.context.active().getValue(experienceTypeKey))
68
+ };
69
+
70
+ // Return activeTraceContext if traceId and spanId are not "undefined"
71
+ return activeTraceContext.traceId !== 'undefined' && activeTraceContext.spanId !== 'undefined' ? activeTraceContext : undefined;
72
+ } else {
73
+ return state.context || undefined;
74
+ }
34
75
  }
76
+
77
+ // DO NOT CALL THIS FUNCTION DIRECTLY!!!!
78
+ // It is only to be called by React UFO libraries for the automatic handling of trace context for experiences.
79
+ // Calling this may cause trace context to be broken
35
80
  function clearActiveTrace() {
36
- state.context = null;
81
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_enable_otel_context_manager')) {
82
+ // Now we need to get the global Context Manager and set the active context
83
+ // Using type assertion because we've "extended" the ContextManager type
84
+ if ((0, _contextManager.getContextManager)() instanceof _contextManager.UFOContextManager) {
85
+ var contextManager = (0, _contextManager.getContextManager)();
86
+
87
+ // ROOT_CONTEXT is an empty context used to initialise ContextManagers
88
+ contextManager.setActive(_api.ROOT_CONTEXT);
89
+ }
90
+ } else {
91
+ state.context = null;
92
+ }
37
93
  }
38
94
  function getActiveTraceHttpRequestHeaders(_url) {
39
- if (state.context === null) {
95
+ if (getActiveTrace() === undefined) {
40
96
  return null;
41
97
  }
42
- var _state$context = state.context,
43
- traceId = _state$context.traceId,
44
- spanId = _state$context.spanId;
98
+ var _ref = getActiveTrace(),
99
+ traceId = _ref.traceId,
100
+ spanId = _ref.spanId;
45
101
  return (0, _makeTraceHttpRequestHeaders.makeTraceHttpRequestHeaders)(traceId, spanId);
46
102
  }
47
103
  function getActiveTraceAsQueryParams(_url) {
@@ -9,11 +9,13 @@ exports.init = init;
9
9
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
10
10
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
11
11
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
12
+ var _api = require("@opentelemetry/api");
12
13
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
13
14
  var _additionalPayload = require("../additional-payload");
14
15
  var _config = require("../config");
15
16
  var _createExperimentalInteractionMetricsPayload = require("../create-experimental-interaction-metrics-payload");
16
17
  var _createExtraSearchPageInteractionPayload = require("../create-extra-search-page-interaction-payload");
18
+ var _contextManager = require("../experience-trace-id-context/context-manager");
17
19
  var _hiddenTiming = require("../hidden-timing");
18
20
  var _interactionMetrics = require("../interaction-metrics");
19
21
  var _interactionsPerformanceObserver = require("../interactions-performance-observer");
@@ -155,6 +157,15 @@ function init(analyticsWebClientAsync, config) {
155
157
  (0, _machineUtilisation.initialisePressureObserver)();
156
158
  (0, _machineUtilisation.initialiseMemoryObserver)();
157
159
  (0, _config.setUFOConfig)(config);
160
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_enable_otel_context_manager')) {
161
+ // Configure global OTel context manager
162
+ var contextManager = new _contextManager.UFOContextManager();
163
+ // set the contextmanager somewhere we can reference it later
164
+ (0, _contextManager.setContextManager)(contextManager);
165
+ // Register the context manager with the global OTel API
166
+ contextManager.enable();
167
+ _api.context.setGlobalContextManager(contextManager);
168
+ }
158
169
  if ((_config$vc = config.vc) !== null && _config$vc !== void 0 && _config$vc.enabled) {
159
170
  var _config$experimentalI, _config$extraInteract;
160
171
  var vcOptions = {
@@ -202,11 +213,10 @@ function init(analyticsWebClientAsync, config) {
202
213
  createTerminalErrorPayloadPackage = _ref3[4];
203
214
  if (awc.getAnalyticsWebClientPromise) {
204
215
  awc.getAnalyticsWebClientPromise().then(function (client) {
205
- var _config$experimentalI2, _config$postInteracti, _config$extraInteract2, _config$extraSearchPa;
216
+ var _config$terminalError, _config$experimentalI2, _config$postInteracti, _config$extraInteract2, _config$extraSearchPa;
206
217
  var instance = client.getInstance();
207
218
  sinkInteraction(instance, payloadPackage);
208
- // TODO: make this configurable
209
- if ((0, _platformFeatureFlags.fg)('platform_ufo_enable_terminal_errors')) {
219
+ if (config !== null && config !== void 0 && (_config$terminalError = config.terminalErrors) !== null && _config$terminalError !== void 0 && _config$terminalError.enabled && (0, _platformFeatureFlags.fg)('platform_ufo_enable_terminal_errors')) {
210
220
  sinkTerminalErrors(instance, createTerminalErrorPayloadPackage.default);
211
221
  }
212
222
  if (config !== null && config !== void 0 && (_config$experimentalI2 = config.experimentalInteractionMetrics) !== null && _config$experimentalI2 !== void 0 && _config$experimentalI2.enabled) {
@@ -223,10 +233,9 @@ function init(analyticsWebClientAsync, config) {
223
233
  }
224
234
  });
225
235
  } else if (awc.sendOperationalEvent) {
226
- var _config$experimentalI3, _config$postInteracti2, _config$extraInteract3, _config$extraSearchPa2;
236
+ var _config$terminalError2, _config$experimentalI3, _config$postInteracti2, _config$extraInteract3, _config$extraSearchPa2;
227
237
  sinkInteraction(awc, payloadPackage);
228
- // TODO: make this configurable
229
- if ((0, _platformFeatureFlags.fg)('platform_ufo_enable_terminal_errors')) {
238
+ if (config !== null && config !== void 0 && (_config$terminalError2 = config.terminalErrors) !== null && _config$terminalError2 !== void 0 && _config$terminalError2.enabled && (0, _platformFeatureFlags.fg)('platform_ufo_enable_terminal_errors')) {
230
239
  sinkTerminalErrors(awc, createTerminalErrorPayloadPackage.default);
231
240
  }
232
241
  if (config !== null && config !== void 0 && (_config$experimentalI3 = config.experimentalInteractionMetrics) !== null && _config$experimentalI3 !== void 0 && _config$experimentalI3.enabled) {
@@ -87,7 +87,7 @@ var VCObserverWrapper = exports.VCObserverWrapper = /*#__PURE__*/function () {
87
87
  startTime: startTime
88
88
  });
89
89
  }
90
- if ((0, _config.isVCRevisionEnabled)('fy25.03', experienceKey)) {
90
+ if ((0, _config.isVCRevisionEnabled)('fy25.03', experienceKey) || (0, _config.isVCRevisionEnabled)('fy26.04', experienceKey)) {
91
91
  var _this$newVCObserver;
92
92
  (_this$newVCObserver = this.newVCObserver) === null || _this$newVCObserver === void 0 || _this$newVCObserver.start({
93
93
  startTime: startTime
@@ -104,7 +104,7 @@ var VCObserverWrapper = exports.VCObserverWrapper = /*#__PURE__*/function () {
104
104
  var _this$oldVCObserver2;
105
105
  (_this$oldVCObserver2 = this.oldVCObserver) === null || _this$oldVCObserver2 === void 0 || _this$oldVCObserver2.stop();
106
106
  }
107
- if ((0, _config.isVCRevisionEnabled)('fy25.03', experienceKey)) {
107
+ if ((0, _config.isVCRevisionEnabled)('fy25.03', experienceKey) || (0, _config.isVCRevisionEnabled)('fy26.04', experienceKey)) {
108
108
  var _this$newVCObserver2;
109
109
  (_this$newVCObserver2 = this.newVCObserver) === null || _this$newVCObserver2 === void 0 || _this$newVCObserver2.stop();
110
110
  }
@@ -122,7 +122,7 @@ var VCObserverWrapper = exports.VCObserverWrapper = /*#__PURE__*/function () {
122
122
  key: "getVCResult",
123
123
  value: function () {
124
124
  var _getVCResult = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(param) {
125
- var _this$oldVCObserver4, _this$newVCObserver3, _ref2;
125
+ var _this$oldVCObserver4, _this$newVCObserver3, _v3v4Result$, _ref2;
126
126
  var experienceKey, include3p, excludeSmartAnswersInSearch, includeSSRRatio, includeRawData, v1v2Result, v3v4Result, ssrRatio;
127
127
  return _regenerator.default.wrap(function _callee$(_context) {
128
128
  while (1) switch (_context.prev = _context.next) {
@@ -142,7 +142,7 @@ var VCObserverWrapper = exports.VCObserverWrapper = /*#__PURE__*/function () {
142
142
  _context.t0 = {};
143
143
  case 8:
144
144
  v1v2Result = _context.t0;
145
- if (!(0, _config.isVCRevisionEnabled)('fy25.03', experienceKey)) {
145
+ if (!((0, _config.isVCRevisionEnabled)('fy25.03', experienceKey) || (0, _config.isVCRevisionEnabled)('fy26.04', experienceKey))) {
146
146
  _context.next = 15;
147
147
  break;
148
148
  }
@@ -170,13 +170,13 @@ var VCObserverWrapper = exports.VCObserverWrapper = /*#__PURE__*/function () {
170
170
  _context.t1 = [];
171
171
  case 16:
172
172
  v3v4Result = _context.t1;
173
- if (v3v4Result) {
173
+ if (!(!v3v4Result || v3v4Result.length === 0)) {
174
174
  _context.next = 19;
175
175
  break;
176
176
  }
177
177
  return _context.abrupt("return", v1v2Result !== null && v1v2Result !== void 0 ? v1v2Result : {});
178
178
  case 19:
179
- ssrRatio = v3v4Result[0].ssrRatio;
179
+ ssrRatio = v3v4Result === null || v3v4Result === void 0 || (_v3v4Result$ = v3v4Result[0]) === null || _v3v4Result$ === void 0 ? void 0 : _v3v4Result$.ssrRatio;
180
180
  return _context.abrupt("return", _objectSpread(_objectSpread(_objectSpread({}, includeSSRRatio && ssrRatio !== undefined ? {
181
181
  'ufo:vc:ssrRatio': ssrRatio
182
182
  } : {}), v1v2Result), {}, {
@@ -0,0 +1,142 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import { ROOT_CONTEXT } from '@opentelemetry/api';
3
+
4
+ /**
5
+ * We need to store a reference to the Context manager so that we can get it later.
6
+ * This is because the OTel JS API doesn't allow us to get the registered context manager,
7
+ * and we need to get it because we need to call a function that's not available in the OTel API:
8
+ * `setActive`. The OTel JS API design doesn't allow us to manually set the active context
9
+ * despite the spec allowing for the capability. Sigh.
10
+ *
11
+ * I imagine this situation might not be permanent if we can move to a system were we can rely
12
+ * solely on the API, but for the purposes of the first change being React UFO API compatible,
13
+ * we'll need to do this.
14
+ */
15
+
16
+ let contextManager;
17
+ export function setContextManager(ctxMgr) {
18
+ contextManager = ctxMgr;
19
+ }
20
+ export function getContextManager() {
21
+ return contextManager;
22
+ }
23
+
24
+ /**
25
+ * The below is shamelessly borrowed from StackContextManager from @opentelemetry/sdk-trace-web
26
+ * Using this rather than importing the entire package because this is all we need for now
27
+ */
28
+
29
+ /**
30
+ * UFO Context Manager for managing the state in web
31
+ * it doesn't fully support the async calls though
32
+ */
33
+ export class UFOContextManager {
34
+ constructor() {
35
+ /**
36
+ * whether the context manager is enabled or not
37
+ */
38
+ _defineProperty(this, "_enabled", false);
39
+ /**
40
+ * Keeps the reference to current context
41
+ */
42
+ _defineProperty(this, "_currentContext", ROOT_CONTEXT);
43
+ }
44
+ /**
45
+ *
46
+ * @param context
47
+ * @param target Function to be executed within the context
48
+ */
49
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
50
+ _bindFunction(context = ROOT_CONTEXT, target) {
51
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
52
+ const manager = this;
53
+ const contextWrapper = function (...args) {
54
+ return manager.with(context, () => target.apply(this, args));
55
+ };
56
+ Object.defineProperty(contextWrapper, 'length', {
57
+ enumerable: false,
58
+ configurable: true,
59
+ writable: false,
60
+ value: target.length
61
+ });
62
+ return contextWrapper;
63
+ }
64
+
65
+ /**
66
+ * Returns the active context
67
+ */
68
+ active() {
69
+ return this._currentContext;
70
+ }
71
+
72
+ /**
73
+ * Binds a the certain context or the active one to the target function and then returns the target
74
+ * @param context A context (span) to be bind to target
75
+ * @param target a function or event emitter. When target or one of its callbacks is called,
76
+ * the provided context will be used as the active context for the duration of the call.
77
+ */
78
+ bind(context, target) {
79
+ // if no specific context to propagate is given, we use the current one
80
+ if (context === undefined) {
81
+ context = this.active();
82
+ }
83
+ if (typeof target === 'function') {
84
+ return this._bindFunction(context, target);
85
+ }
86
+ return target;
87
+ }
88
+
89
+ /**
90
+ * Disable the context manager (clears the current context)
91
+ */
92
+ disable() {
93
+ this._currentContext = ROOT_CONTEXT;
94
+ this._enabled = false;
95
+ return this;
96
+ }
97
+
98
+ /**
99
+ * Enables the context manager and creates a default(root) context
100
+ */
101
+ enable() {
102
+ if (this._enabled) {
103
+ return this;
104
+ }
105
+ this._enabled = true;
106
+ this._currentContext = ROOT_CONTEXT;
107
+ return this;
108
+ }
109
+
110
+ /**
111
+ * Calls the callback function [fn] with the provided [context]. If [context] is undefined then it will use the window.
112
+ * The context will be set as active
113
+ * @param context
114
+ * @param fn Callback function
115
+ * @param thisArg optional receiver to be used for calling fn
116
+ * @param args optional arguments forwarded to fn
117
+ */
118
+ with(context, fn, thisArg, ...args) {
119
+ const previousContext = this._currentContext;
120
+ this._currentContext = context || ROOT_CONTEXT;
121
+ try {
122
+ return fn.call(thisArg, ...args);
123
+ } finally {
124
+ this._currentContext = previousContext;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Sets the active context.
130
+ * This function is an extension of the OTel spec, in order to facilitate the current API of React UFO trace context handling.
131
+ * It doesn't keep track of any previously set active contexts, because it's assumed (for now) that only one trace context
132
+ * will ever exist at a time.
133
+ * The next step in the work to improve tracing in the FE will likely remove this function as we need to track the
134
+ * hierarchy of contexts (likely using the `with()` function above).
135
+ * @param context The context to be made the globally active one
136
+ */
137
+ setActive(context) {
138
+ if (this._enabled) {
139
+ this._currentContext = context;
140
+ }
141
+ }
142
+ }
@@ -1,34 +1,90 @@
1
+ import { context, createContextKey, ROOT_CONTEXT } from '@opentelemetry/api';
2
+ import { fg } from '@atlaskit/platform-feature-flags';
3
+ import { getContextManager, UFOContextManager } from './context-manager';
1
4
  import { makeTraceHttpRequestHeaders } from './utils/make-trace-http-request-headers';
2
5
  const state = {
3
6
  context: null
4
7
  };
8
+ const traceIdKey = createContextKey("traceId");
9
+ const spanIdKey = createContextKey("spanId");
10
+ const experienceTypeKey = createContextKey("type");
11
+
12
+ // DO NOT CALL THIS FUNCTION DIRECTLY!!!!
13
+ // It is only to be called by React UFO libraries for the automatic handling of trace context for experiences.
14
+ // Calling this may cause trace context to be broken
5
15
  export function generateSpanId() {
6
16
  return Array.from(new Array(16), () => Math.floor(Math.random() * 16).toString(16)).join('');
7
17
  }
18
+
19
+ // DO NOT CALL THIS FUNCTION DIRECTLY!!!!
20
+ // It is only to be called by React UFO libraries for the automatic handling of trace context for experiences.
21
+ // Calling this may cause trace context to be broken
8
22
  export function setInteractionActiveTrace(interactionId, experienceType) {
9
23
  setActiveTrace(interactionId.replace(/-/g, ''), generateSpanId(), experienceType);
10
24
  }
25
+
26
+ // DO NOT CALL THIS FUNCTION DIRECTLY!!!!
27
+ // It is only to be called by React UFO libraries for the automatic handling of trace context for experiences.
28
+ // Calling this may cause trace context to be broken
11
29
  export function setActiveTrace(traceId, spanId, type) {
12
- state.context = {
13
- traceId,
14
- spanId,
15
- type
16
- };
30
+ if (fg('platform_ufo_enable_otel_context_manager')) {
31
+ const activeTraceContext = ROOT_CONTEXT.setValue(traceIdKey, traceId).setValue(spanIdKey, spanId).setValue(experienceTypeKey, type);
32
+
33
+ // Now we need to get the global Context Manager and set the active context
34
+ // Using type assertion because we've "extended" the ContextManager type
35
+ if (getContextManager() instanceof UFOContextManager) {
36
+ let contextManager = getContextManager();
37
+ contextManager.setActive(activeTraceContext);
38
+ }
39
+ } else {
40
+ state.context = {
41
+ traceId,
42
+ spanId,
43
+ type
44
+ };
45
+ }
17
46
  }
18
47
  export function getActiveTrace() {
19
- return state.context || undefined;
48
+ if (fg('platform_ufo_enable_otel_context_manager')) {
49
+ // Get trace context from active context
50
+ const activeTraceContext = {
51
+ traceId: String(context.active().getValue(traceIdKey)),
52
+ spanId: String(context.active().getValue(spanIdKey)),
53
+ type: String(context.active().getValue(experienceTypeKey))
54
+ };
55
+
56
+ // Return activeTraceContext if traceId and spanId are not "undefined"
57
+ return activeTraceContext.traceId !== 'undefined' && activeTraceContext.spanId !== 'undefined' ? activeTraceContext : undefined;
58
+ } else {
59
+ return state.context || undefined;
60
+ }
20
61
  }
62
+
63
+ // DO NOT CALL THIS FUNCTION DIRECTLY!!!!
64
+ // It is only to be called by React UFO libraries for the automatic handling of trace context for experiences.
65
+ // Calling this may cause trace context to be broken
21
66
  export function clearActiveTrace() {
22
- state.context = null;
67
+ if (fg('platform_ufo_enable_otel_context_manager')) {
68
+ // Now we need to get the global Context Manager and set the active context
69
+ // Using type assertion because we've "extended" the ContextManager type
70
+ if (getContextManager() instanceof UFOContextManager) {
71
+ let contextManager = getContextManager();
72
+
73
+ // ROOT_CONTEXT is an empty context used to initialise ContextManagers
74
+ contextManager.setActive(ROOT_CONTEXT);
75
+ }
76
+ } else {
77
+ state.context = null;
78
+ }
23
79
  }
24
80
  export function getActiveTraceHttpRequestHeaders(_url) {
25
- if (state.context === null) {
81
+ if (getActiveTrace() === undefined) {
26
82
  return null;
27
83
  }
28
84
  const {
29
85
  traceId,
30
86
  spanId
31
- } = state.context;
87
+ } = getActiveTrace();
32
88
  return makeTraceHttpRequestHeaders(traceId, spanId);
33
89
  }
34
90
  export function getActiveTraceAsQueryParams(_url) {