@dynatrace/react-native-plugin 2.321.2 → 2.323.2

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.
Files changed (39) hide show
  1. package/README.md +54 -17
  2. package/android/build.gradle +3 -3
  3. package/files/plugin-runtime.gradle +8 -1
  4. package/files/plugin.gradle +1 -1
  5. package/index.js +1 -0
  6. package/instrumentation/DynatraceInstrumentation.js +1 -1
  7. package/instrumentation/jsx/CreateElement.js +1 -1
  8. package/instrumentation/jsx/ElementHelper.js +2 -21
  9. package/instrumentation/jsx/IDynatraceProperties.js +0 -7
  10. package/instrumentation/jsx/JsxDevRuntime.js +2 -7
  11. package/instrumentation/jsx/JsxRuntime.js +2 -7
  12. package/instrumentation/jsx/components/ComponentUtil.js +12 -1
  13. package/instrumentation/jsx/components/Picker.js +3 -3
  14. package/instrumentation/jsx/components/RefreshControl.js +3 -3
  15. package/instrumentation/libs/community/gesture-handler/Touchables.js +7 -37
  16. package/instrumentation/libs/react-native/Switch.js +55 -12
  17. package/instrumentation/libs/react-native/Touchables.js +8 -47
  18. package/instrumentation/libs/withOnPressMonitoring.js +121 -0
  19. package/instrumentation/model/Types.js +3 -15
  20. package/instrumentation/model/TypesUtil.js +5 -34
  21. package/lib/core/Dynatrace.js +4 -1
  22. package/lib/core/ErrorHandler.js +18 -34
  23. package/lib/next/Dynatrace.js +16 -10
  24. package/lib/next/events/EventCreator.js +2 -1
  25. package/lib/next/events/HttpRequestEventBuilder.js +196 -0
  26. package/lib/next/events/ViewInfoCreator.js +1 -1
  27. package/lib/next/events/modifier/EventModifierUtil.js +14 -1
  28. package/lib/next/events/modifier/SendEventValidation.js +23 -10
  29. package/lib/next/events/spec/EventSpecContstants.js +1 -1
  30. package/package.json +6 -4
  31. package/react-native-dynatrace.podspec +1 -1
  32. package/scripts/Ios.js +1 -1
  33. package/scripts/Logger.js +20 -1
  34. package/scripts/core/InstrumentCall.js +62 -34
  35. package/scripts/util/SourceMapUtil.js +4 -4
  36. package/src/instrumentation/jsx/IDynatraceProperties.ts +15 -0
  37. package/typings/react-native-dynatrace.d.ts +130 -1
  38. package/instrumentation/jsx/components/Switch.js +0 -57
  39. package/instrumentation/jsx/components/Touchable.js +0 -151
@@ -1,51 +1,12 @@
1
1
  "use strict";
2
- var _a, _b, _c, _d;
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.Pressable = exports.Text = exports.TouchableWithoutFeedback = exports.TouchableNativeFeedback = exports.TouchableHighlight = exports.TouchableOpacity = exports.Button = void 0;
5
4
  const ReactNative = require("react-native");
6
- const React = require("react");
7
- const Types_1 = require("../../model/Types");
8
- exports.Button = React.forwardRef((props, ref) => (React.createElement(ReactNative.Button, Object.assign({ title: props.title }, props, { ref: ref }))));
9
- exports.Button._dtInfo = { type: Types_1.Types.Button };
10
- if (typeof ReactNative.TouchableOpacity === 'object') {
11
- exports.TouchableOpacity = Object.assign({ _dtInfo: { type: Types_1.Types.TouchableOpacity } }, ReactNative.TouchableOpacity);
12
- }
13
- else {
14
- exports.TouchableOpacity = (_a = class TouchableOpacity extends (ReactNative.TouchableOpacity) {
15
- },
16
- _a._dtInfo = { type: Types_1.Types.TouchableOpacity },
17
- _a);
18
- }
19
- if (typeof ReactNative.TouchableHighlight === 'object') {
20
- exports.TouchableHighlight = Object.assign({ _dtInfo: { type: Types_1.Types.TouchableHighlight } }, ReactNative.TouchableHighlight);
21
- }
22
- else {
23
- exports.TouchableHighlight = (_b = class TouchableHighlight extends (ReactNative.TouchableHighlight) {
24
- },
25
- _b._dtInfo = { type: Types_1.Types.TouchableHighlight },
26
- _b);
27
- }
28
- if (typeof ReactNative.TouchableNativeFeedback === 'object') {
29
- exports.TouchableNativeFeedback = Object.assign({ _dtInfo: { type: Types_1.Types.TouchableNativeFeedback } }, ReactNative.TouchableNativeFeedback);
30
- }
31
- else {
32
- exports.TouchableNativeFeedback = (_c = class TouchableNativeFeedback extends (ReactNative.TouchableNativeFeedback) {
33
- },
34
- _c._dtInfo = { type: Types_1.Types.TouchableNativeFeedback },
35
- _c);
36
- }
37
- exports.TouchableWithoutFeedback = React.forwardRef((props, ref) => (React.createElement(ReactNative.TouchableWithoutFeedback, Object.assign({}, props, { ref: ref }))));
38
- exports.TouchableWithoutFeedback._dtInfo = {
39
- type: Types_1.Types.TouchableWithoutFeedback,
40
- };
41
- if (typeof ReactNative.Text === 'object') {
42
- exports.Text = Object.assign({ _dtInfo: { type: Types_1.Types.Text } }, ReactNative.Text);
43
- }
44
- else {
45
- exports.Text = (_d = class Text extends ReactNative.Text {
46
- },
47
- _d._dtInfo = { type: Types_1.Types.Text },
48
- _d);
49
- }
50
- exports.Pressable = React.forwardRef((props, ref) => (React.createElement(ReactNative.Pressable, Object.assign({}, props, { ref: ref }))));
51
- exports.Pressable._dtInfo = { type: Types_1.Types.Pressable };
5
+ const withOnPressMonitoring_1 = require("../withOnPressMonitoring");
6
+ exports.Button = (0, withOnPressMonitoring_1.withOnPressMonitoring)(ReactNative.Button, 'Button');
7
+ exports.TouchableOpacity = (0, withOnPressMonitoring_1.withOnPressMonitoring)(ReactNative.TouchableOpacity, 'Touchable');
8
+ exports.TouchableHighlight = (0, withOnPressMonitoring_1.withOnPressMonitoring)(ReactNative.TouchableHighlight, 'Touchable');
9
+ exports.TouchableNativeFeedback = (0, withOnPressMonitoring_1.withOnPressMonitoring)(ReactNative.TouchableNativeFeedback, 'Touchable');
10
+ exports.TouchableWithoutFeedback = (0, withOnPressMonitoring_1.withOnPressMonitoring)(ReactNative.TouchableWithoutFeedback, 'Touchable');
11
+ exports.Text = (0, withOnPressMonitoring_1.withOnPressMonitoring)(ReactNative.Text, 'Text');
12
+ exports.Pressable = (0, withOnPressMonitoring_1.withOnPressMonitoring)(ReactNative.Pressable, 'Pressable');
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findTouchableName = exports.chooseTouchableName = exports.wrapOnPress = exports.withOnPressMonitoring = void 0;
4
+ const ReactNative = require("react-native");
5
+ const React = require("react");
6
+ const ConsoleLogger_1 = require("../../lib/core/logging/ConsoleLogger");
7
+ const ConfigurationHandler_1 = require("../../lib/core/configuration/ConfigurationHandler");
8
+ const Dynatrace_1 = require("../../lib/core/Dynatrace");
9
+ const ComponentUtil_1 = require("../jsx/components/ComponentUtil");
10
+ const Logger = new ConsoleLogger_1.ConsoleLogger('TouchableHelper');
11
+ function withOnPressMonitoring(Component, fallbackName) {
12
+ return React.forwardRef((props, ref) => {
13
+ const { onPress, onLongPress } = props;
14
+ const touchableName = (0, exports.chooseTouchableName)(props, fallbackName);
15
+ const dynatraceIgnored = (0, ComponentUtil_1.isDynatraceIgnored)(props);
16
+ const propsWithoutRef = Object.assign(Object.assign(Object.assign({}, props), (!dynatraceIgnored &&
17
+ onPress && {
18
+ onPress: (0, exports.wrapOnPress)(onPress, touchableName),
19
+ })), (!dynatraceIgnored &&
20
+ onLongPress && {
21
+ onLongPress: (0, exports.wrapOnPress)(onLongPress, touchableName),
22
+ }));
23
+ const propsWithRef = Object.assign(Object.assign({}, propsWithoutRef), { ref });
24
+ return React.createElement(Component, Object.assign({}, propsWithRef));
25
+ });
26
+ }
27
+ exports.withOnPressMonitoring = withOnPressMonitoring;
28
+ const wrapOnPress = (onPress, touchableName) => {
29
+ return (...args) => {
30
+ if (!ConfigurationHandler_1.ConfigurationHandler.isConfigurationAvailable()) {
31
+ Logger.info('React Native plugin has not been started yet! Touch will not be reported!');
32
+ onPress(...args);
33
+ }
34
+ else {
35
+ const action = Dynatrace_1.Dynatrace.enterAutoAction(`Touch on ${touchableName}`);
36
+ let isSync = true;
37
+ try {
38
+ const returnValue = onPress(...args);
39
+ if (returnValue instanceof Promise) {
40
+ isSync = false;
41
+ return Promise.resolve(returnValue).finally(() => {
42
+ action.leaveAction();
43
+ });
44
+ }
45
+ }
46
+ finally {
47
+ if (isSync) {
48
+ action.leaveAction();
49
+ }
50
+ }
51
+ }
52
+ };
53
+ };
54
+ exports.wrapOnPress = wrapOnPress;
55
+ const chooseTouchableName = (props, fallbackName) => {
56
+ if (props.dtActionName) {
57
+ return props.dtActionName;
58
+ }
59
+ if (ConfigurationHandler_1.ConfigurationHandler.isConfigurationAvailable() &&
60
+ ConfigurationHandler_1.ConfigurationHandler.isActionNamePrivacyEnabled()) {
61
+ return fallbackName;
62
+ }
63
+ return (0, exports.findTouchableName)(props) || fallbackName;
64
+ };
65
+ exports.chooseTouchableName = chooseTouchableName;
66
+ const findTouchableName = (props) => {
67
+ if ('title' in props) {
68
+ return props.title;
69
+ }
70
+ if (props.accessibilityLabel !== undefined) {
71
+ return props.accessibilityLabel;
72
+ }
73
+ if (typeof props.children === 'function') {
74
+ return null;
75
+ }
76
+ return findTouchableNameRecursive(props.children);
77
+ };
78
+ exports.findTouchableName = findTouchableName;
79
+ function findTouchableNameRecursive(node) {
80
+ if (typeof node === 'string') {
81
+ return node;
82
+ }
83
+ if (isReactNodeIterable(node)) {
84
+ for (const child of node) {
85
+ const result = findTouchableNameRecursive(child);
86
+ if (result !== null)
87
+ return result;
88
+ }
89
+ }
90
+ if (React.isValidElement(node)) {
91
+ if (isReactNativeImage(node)) {
92
+ const uri = getUriFromSource(node.props.source);
93
+ return uri ? 'Image Button: ' + uri : 'Image Button';
94
+ }
95
+ if (isCustomIcon(node)) {
96
+ return node.props.name;
97
+ }
98
+ if (hasReactNodeChildren(node))
99
+ return findTouchableNameRecursive(node.props.children);
100
+ }
101
+ return null;
102
+ }
103
+ function getUriFromSource(source) {
104
+ if (typeof source === 'object' &&
105
+ 'uri' in source &&
106
+ source.uri !== undefined) {
107
+ return source.uri;
108
+ }
109
+ return null;
110
+ }
111
+ const isReactNodeIterable = (node) => typeof node === 'object' && node !== null && Symbol.iterator in node;
112
+ const isReactNativeImage = (element) => element.type === ReactNative.Image;
113
+ const isCustomIcon = (element) => typeof element.type === 'function' &&
114
+ element.type.name === 'Icon' &&
115
+ typeof element.props === 'object' &&
116
+ element.props !== null &&
117
+ 'name' in element.props &&
118
+ typeof element.props.name === 'string';
119
+ const hasReactNodeChildren = (element) => typeof element.props === 'object' &&
120
+ element.props !== null &&
121
+ 'children' in element.props;
@@ -5,19 +5,7 @@ var Types;
5
5
  (function (Types) {
6
6
  Types[Types["FunctionalComponent"] = 0] = "FunctionalComponent";
7
7
  Types[Types["ClassComponent"] = 1] = "ClassComponent";
8
- Types[Types["Button"] = 2] = "Button";
9
- Types[Types["TouchableOpacity"] = 3] = "TouchableOpacity";
10
- Types[Types["TouchableHighlight"] = 4] = "TouchableHighlight";
11
- Types[Types["TouchableNativeFeedback"] = 5] = "TouchableNativeFeedback";
12
- Types[Types["TouchableWithoutFeedback"] = 6] = "TouchableWithoutFeedback";
13
- Types[Types["Text"] = 7] = "Text";
14
- Types[Types["Picker"] = 8] = "Picker";
15
- Types[Types["Pressable"] = 9] = "Pressable";
16
- Types[Types["TouchableOpacityGestureHandler"] = 10] = "TouchableOpacityGestureHandler";
17
- Types[Types["TouchableHighlightGestureHandler"] = 11] = "TouchableHighlightGestureHandler";
18
- Types[Types["TouchableNativeFeedbackGestureHandler"] = 12] = "TouchableNativeFeedbackGestureHandler";
19
- Types[Types["TouchableWithoutFeedbackGestureHandler"] = 13] = "TouchableWithoutFeedbackGestureHandler";
20
- Types[Types["RectButtonGestureHandler"] = 14] = "RectButtonGestureHandler";
21
- Types[Types["RefreshControl"] = 15] = "RefreshControl";
22
- Types[Types["Switch"] = 16] = "Switch";
8
+ Types[Types["Picker"] = 2] = "Picker";
9
+ Types[Types["RefreshControl"] = 3] = "RefreshControl";
10
+ Types[Types["Switch"] = 4] = "Switch";
23
11
  })(Types = exports.Types || (exports.Types = {}));
@@ -1,48 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getNameForType = exports.isTypeTouchable = exports.isTypeGestureHandlerTouchable = exports.isTypeReactNativeTouchable = void 0;
3
+ exports.getNameForType = void 0;
4
4
  const Types_1 = require("./Types");
5
- const isTypeReactNativeTouchable = (type) => type === Types_1.Types.TouchableHighlight ||
6
- type === Types_1.Types.TouchableNativeFeedback ||
7
- type === Types_1.Types.TouchableOpacity ||
8
- type === Types_1.Types.TouchableWithoutFeedback;
9
- exports.isTypeReactNativeTouchable = isTypeReactNativeTouchable;
10
- const isTypeGestureHandlerTouchable = (type) => type === Types_1.Types.TouchableHighlightGestureHandler ||
11
- type === Types_1.Types.TouchableNativeFeedbackGestureHandler ||
12
- type === Types_1.Types.TouchableOpacityGestureHandler ||
13
- type === Types_1.Types.TouchableWithoutFeedbackGestureHandler ||
14
- type === Types_1.Types.RectButtonGestureHandler;
15
- exports.isTypeGestureHandlerTouchable = isTypeGestureHandlerTouchable;
16
- const isTypeTouchable = (type) => (0, exports.isTypeGestureHandlerTouchable)(type) || (0, exports.isTypeReactNativeTouchable)(type);
17
- exports.isTypeTouchable = isTypeTouchable;
18
5
  const getNameForType = (type) => {
19
6
  switch (type) {
20
- case Types_1.Types.Button:
21
- case Types_1.Types.RectButtonGestureHandler:
22
- return 'Button';
23
- case Types_1.Types.Text:
24
- return 'Text';
25
- case Types_1.Types.TouchableHighlight:
26
- case Types_1.Types.TouchableNativeFeedback:
27
- case Types_1.Types.TouchableOpacity:
28
- case Types_1.Types.TouchableWithoutFeedback:
29
- case Types_1.Types.TouchableHighlightGestureHandler:
30
- case Types_1.Types.TouchableNativeFeedbackGestureHandler:
31
- case Types_1.Types.TouchableOpacityGestureHandler:
32
- case Types_1.Types.TouchableWithoutFeedbackGestureHandler:
33
- return 'Touchable';
34
- case Types_1.Types.Pressable:
35
- return 'Pressable';
7
+ case Types_1.Types.FunctionalComponent:
8
+ return 'Functional Component';
9
+ case Types_1.Types.ClassComponent:
10
+ return 'Class Component';
36
11
  case Types_1.Types.Picker:
37
12
  return 'Picker';
38
13
  case Types_1.Types.RefreshControl:
39
14
  return 'Refresh Control';
40
15
  case Types_1.Types.Switch:
41
16
  return 'Switch';
42
- case Types_1.Types.ClassComponent:
43
- return 'Class Component';
44
- case Types_1.Types.FunctionalComponent:
45
- return 'Functional Component';
46
17
  default:
47
18
  return 'Undefined';
48
19
  }
@@ -33,7 +33,7 @@ exports.Dynatrace = {
33
33
  if (configuration !== undefined) {
34
34
  yield DynatraceBridge_1.DynatraceNative.start(configuration);
35
35
  if (configuration.errorHandler) {
36
- (0, ErrorHandler_1.registerErrorHandler)(configuration.reportFatalErrorAsCrash);
36
+ (0, ErrorHandler_1.startErrorHandler)(configuration.reportFatalErrorAsCrash);
37
37
  }
38
38
  else {
39
39
  logger.debug('Dynatrace error handler will not be enabled as errorHandler was set to false!');
@@ -353,4 +353,7 @@ exports.Dynatrace = {
353
353
  sendExceptionEvent: (error, fields) => {
354
354
  Dynatrace_1.Dynatrace.sendExceptionEvent(error, fields);
355
355
  },
356
+ sendHttpRequestEvent(httpRequestEventBuilder) {
357
+ Dynatrace_1.Dynatrace.sendHttpRequestEvent(httpRequestEventBuilder);
358
+ },
356
359
  };
@@ -1,56 +1,40 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._reportErrorToDynatrace = exports.registerErrorHandler = void 0;
4
- const react_native_1 = require("react-native");
3
+ exports.reportErrorToDynatrace = exports.startErrorHandler = void 0;
5
4
  const DynatraceInternal_1 = require("./DynatraceInternal");
6
5
  const Dynatrace_1 = require("./Dynatrace");
7
6
  const StringUtils_1 = require("./util/StringUtils");
8
7
  const ConsoleLogger_1 = require("./logging/ConsoleLogger");
9
- let _isReportFatalErrorAsCrash = true;
10
- const _isReactNativeGlobal = (globalScope) => globalScope.ErrorUtils !== undefined;
11
8
  const logger = new ConsoleLogger_1.ConsoleLogger('ErrorHandler');
12
- const registerErrorHandler = (reportFatalErrorAsCrash) => {
13
- if (global !== undefined && _isReactNativeGlobal(global)) {
14
- _isReportFatalErrorAsCrash = reportFatalErrorAsCrash;
15
- const oldHandler = global.ErrorUtils.getGlobalHandler();
16
- global.ErrorUtils.setGlobalHandler((error, isFatal) => {
17
- (0, exports._reportErrorToDynatrace)(error, isFatal, oldHandler);
18
- });
19
- const setter = global.ErrorUtils.setGlobalHandler;
20
- global.ErrorUtils.setGlobalHandler = (errorHandler) => {
21
- setter((error, isFatal) => {
22
- (0, exports._reportErrorToDynatrace)(error, isFatal, errorHandler);
23
- });
24
- };
25
- }
9
+ let manualStart = { enabled: false };
10
+ const startErrorHandler = (reportFatalErrorAsCrash) => {
11
+ manualStart = {
12
+ enabled: true,
13
+ reportFatalErrorAsCrash: reportFatalErrorAsCrash,
14
+ };
26
15
  };
27
- exports.registerErrorHandler = registerErrorHandler;
28
- const _reportErrorToDynatrace = (exception, isFatal, oldHandler) => {
16
+ exports.startErrorHandler = startErrorHandler;
17
+ const reportErrorToDynatrace = (exception, isFatal, reportFatalErrorAsCrashFromConfig, autoStart) => {
18
+ if (!autoStart && !manualStart)
19
+ return;
20
+ const reportFatalErrorAsCrash = manualStart.enabled
21
+ ? manualStart.reportFatalErrorAsCrash
22
+ : reportFatalErrorAsCrashFromConfig;
29
23
  if (exception != null && isExceptionAnError(exception)) {
30
24
  if (!StringUtils_1.StringUtils.isStringNullEmptyOrUndefined(exception.name)) {
31
25
  if (isFatal === undefined) {
32
26
  isFatal = false;
33
27
  }
34
- DynatraceInternal_1.DynatraceInternal.reportErrorFromHandler(isFatal, String(exception.name), adjustedReason(exception.message), exception.stack != null ? exception.stack : '', _isReportFatalErrorAsCrash);
35
- logger.debug(`_reportErrorToDynatrace(${exception}, ${isFatal})`);
28
+ DynatraceInternal_1.DynatraceInternal.reportErrorFromHandler(isFatal, String(exception.name), adjustedReason(exception.message), exception.stack != null ? exception.stack : '', reportFatalErrorAsCrash);
29
+ logger.debug(`reportErrorToDynatrace(${exception}, ${isFatal})`);
36
30
  }
37
31
  }
38
32
  else {
39
33
  Dynatrace_1.Dynatrace.reportError(String(exception), -1);
40
- logger.debug(`_reportErrorToDynatrace(${exception}, -1)`);
41
- }
42
- if (oldHandler !== undefined) {
43
- if (react_native_1.Platform.OS === 'ios') {
44
- setTimeout(() => {
45
- oldHandler(exception, isFatal);
46
- }, 200);
47
- }
48
- else {
49
- oldHandler(exception, isFatal);
50
- }
34
+ logger.debug(`reportErrorToDynatrace(${exception}, -1)`);
51
35
  }
52
36
  };
53
- exports._reportErrorToDynatrace = _reportErrorToDynatrace;
37
+ exports.reportErrorToDynatrace = reportErrorToDynatrace;
54
38
  const adjustedReason = (reason) => {
55
39
  if (!StringUtils_1.StringUtils.isStringNullEmptyOrUndefined(reason)) {
56
40
  const reasonNewLineIndex = reason.indexOf('\n');
@@ -42,8 +42,8 @@ class DynatraceImpl {
42
42
  const eventTimestamp = new EventTimestamp_1.EventTimestamp(this.timestampProvider);
43
43
  const event = Object.assign(Object.assign({}, (0, EventCreator_1.createCrashEvent)(crash.name, crash.message, crash.stack, isFatal)), eventTimestamp.getEventTimeInfo());
44
44
  if (isApiReported) {
45
- event["characteristics.is_api_reported"] =
46
- true;
45
+ (0, EventModifierUtil_1.addIsApiReported)(event);
46
+ event["dt.support.is_legacy_api_reported"] = true;
47
47
  }
48
48
  EventPipeline_1.EventPipeline.insertEvent(event);
49
49
  }
@@ -52,8 +52,8 @@ class DynatraceImpl {
52
52
  const eventTimestamp = new EventTimestamp_1.EventTimestamp(this.timestampProvider);
53
53
  const event = Object.assign(Object.assign({}, (0, EventCreator_1.createErrorCodeEvent)(errorName, errorCode)), eventTimestamp.getEventTimeInfo());
54
54
  if (isApiReported) {
55
- event["characteristics.is_api_reported"] =
56
- true;
55
+ (0, EventModifierUtil_1.addIsApiReported)(event);
56
+ event["dt.support.is_legacy_api_reported"] = true;
57
57
  }
58
58
  EventPipeline_1.EventPipeline.insertEvent(event);
59
59
  }
@@ -62,8 +62,8 @@ class DynatraceImpl {
62
62
  const eventTimestamp = new EventTimestamp_1.EventTimestamp(this.timestampProvider);
63
63
  const event = Object.assign(Object.assign({}, (0, EventCreator_1.createErrorEvent)(error.name, error.message, error.stack)), eventTimestamp.getEventTimeInfo());
64
64
  if (isApiReported) {
65
- event["characteristics.is_api_reported"] =
66
- true;
65
+ (0, EventModifierUtil_1.addIsApiReported)(event);
66
+ event["dt.support.is_legacy_api_reported"] = true;
67
67
  }
68
68
  EventPipeline_1.EventPipeline.insertEvent(event);
69
69
  }
@@ -73,15 +73,14 @@ class DynatraceImpl {
73
73
  const event = Object.assign({}, (0, EventCreator_1.createErrorEvent)(error.name, error.message, error.stack));
74
74
  let eventValidated = SendEventValidation_1.SendEventValidation.modifyEvent(fieldsCheck);
75
75
  eventValidated = Object.assign(Object.assign({}, event), eventValidated);
76
- eventValidated["characteristics.is_api_reported"] = true;
76
+ (0, EventModifierUtil_1.addIsApiReported)(eventValidated);
77
77
  EventPipeline_1.EventPipeline.insertEvent(eventValidated);
78
78
  }
79
79
  sendEvent(properties, context) {
80
80
  this.logger.debug(`sendEvent(${JSON.stringify(properties, context)})`);
81
81
  const eventValidated = SendEventValidation_1.SendEventValidation.modifyEvent(properties);
82
- if (eventValidated != null && (0, EventModifierUtil_1.containEventProperties)(eventValidated)) {
83
- eventValidated["characteristics.has_event_properties"] = true;
84
- }
82
+ (0, EventModifierUtil_1.flagEventProperties)(eventValidated);
83
+ (0, EventModifierUtil_1.addIsApiReported)(eventValidated);
85
84
  EventPipeline_1.EventPipeline.insertEvent(eventValidated, context);
86
85
  }
87
86
  sendSessionPropertyEvent(properties) {
@@ -91,7 +90,14 @@ class DynatraceImpl {
91
90
  (0, EventModifierUtil_1.containSessionProperties)(eventValidated)) {
92
91
  eventValidated["characteristics.has_session_properties"] = true;
93
92
  }
93
+ (0, EventModifierUtil_1.addIsApiReported)(eventValidated);
94
94
  EventPipeline_1.EventPipeline.insertEvent(eventValidated);
95
95
  }
96
+ sendHttpRequestEvent(httpRequestEventBuilder) {
97
+ const sanitizedEvent = httpRequestEventBuilder.build();
98
+ this.logger.debug(`sendHttpRequestEvent(${JSON.stringify(sanitizedEvent)})`);
99
+ (0, EventModifierUtil_1.flagEventProperties)(sanitizedEvent);
100
+ EventPipeline_1.EventPipeline.insertEvent(sanitizedEvent);
101
+ }
96
102
  }
97
103
  exports.Dynatrace = new DynatraceImpl(TimestampProvider_1.defaultTimestampProvider);
@@ -26,7 +26,8 @@ const createErrorCodeEvent = (errorName, errorCode) => {
26
26
  'error.is_fatal': false,
27
27
  };
28
28
  if (errorName != null) {
29
- event["name"] = errorName;
29
+ event["error.name"] = errorName;
30
+ event["error.has_custom_name"] = true;
30
31
  }
31
32
  if (errorCode != null) {
32
33
  event["error.code"] = errorCode;
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const ConsoleLogger_1 = require("../../core/logging/ConsoleLogger");
4
+ const SendEventValidation_1 = require("./modifier/SendEventValidation");
5
+ const TimestampProvider_1 = require("../provider/TimestampProvider");
6
+ const logger = new ConsoleLogger_1.ConsoleLogger('Dynatrace');
7
+ class HttpRequestEventBuilder {
8
+ constructor(url, requestMethod) {
9
+ this.url = url;
10
+ this.requestMethod = requestMethod;
11
+ this.duration = 0;
12
+ this.rawEventProperties = {};
13
+ this.hasDroppedTraceparent = false;
14
+ this.hasDroppedCustomProperties = false;
15
+ this.hasNfnValues = false;
16
+ this.triedToOverwriteDuration = false;
17
+ this.requestMethod =
18
+ requestMethod.toUpperCase();
19
+ }
20
+ withDuration(duration) {
21
+ this.duration = duration;
22
+ this.triedToOverwriteDuration = true;
23
+ return this;
24
+ }
25
+ withStatusCode(statusCode) {
26
+ this.statusCode = statusCode;
27
+ return this;
28
+ }
29
+ withReasonPhrase(reasonPhrase) {
30
+ this.reasonPhrase = reasonPhrase;
31
+ return this;
32
+ }
33
+ withError(error) {
34
+ this.error = error;
35
+ return this;
36
+ }
37
+ withBytesSent(bytesSent) {
38
+ this.bytesSent = bytesSent;
39
+ return this;
40
+ }
41
+ withBytesReceived(bytesReceived) {
42
+ this.bytesReceived = bytesReceived;
43
+ return this;
44
+ }
45
+ withTraceparentHeader(traceparentHeader) {
46
+ this.traceparentHeader = traceparentHeader;
47
+ return this;
48
+ }
49
+ addEventProperty(key, value) {
50
+ this.rawEventProperties[key] = value;
51
+ return this;
52
+ }
53
+ build() {
54
+ if (!this.hasValidMandatoryAttriutes()) {
55
+ logger.debug('HttpRequestEventBuilder dropped invalid event');
56
+ return null;
57
+ }
58
+ this.sanitizeDuration();
59
+ this.sanitizeStatusCode();
60
+ this.sanitizeReasonPhrase();
61
+ this.sanitizeBytesReceived();
62
+ this.sanitizeBytesSent();
63
+ const parsedTraceparentHeader = this.traceparentHeader &&
64
+ this.parseTraceparent(this.traceparentHeader);
65
+ this.hasDroppedTraceparent =
66
+ !!this.traceparentHeader && !parsedTraceparentHeader;
67
+ const hasFailedRequest = this.isStatusCodeError() || !!this.error;
68
+ const filteredEventProperties = Object.fromEntries(Object.entries(this.rawEventProperties).filter(this.isEventPropertyKey, this));
69
+ const sanitizedEventProperties = SendEventValidation_1.SendEventValidation.modifyEvent(filteredEventProperties);
70
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, sanitizedEventProperties), { ["url.full"]: this.url, ["network.protocol.name"]: 'http', ["http.request.method"]: this.requestMethod, ["duration"]: this.duration, ["start_time"]: TimestampProvider_1.defaultTimestampProvider.getCurrentTimestamp() - this.duration }), (this.triedToOverwriteDuration && {
71
+ ["dt.support.api.overridden_fields"]: [
72
+ 'duration',
73
+ ],
74
+ })), this.includeIfDefined("http.response.status_code", this.statusCode)), this.includeIfDefined("http.response.reason_phrase", this.reasonPhrase)), this.includeIfDefined("request.bytes_sent", this.bytesSent)), this.includeIfDefined("request.bytes_received", this.bytesReceived)), { ["characteristics.has_request"]: true, ["characteristics.is_api_reported"]: true }), this.includeIfTrue("characteristics.has_failed_request", hasFailedRequest)), this.includeIfTrue("characteristics.has_error", hasFailedRequest)), (this.error !== undefined && Object.assign({ ["characteristics.has_exception"]: true, ["exception.type"]: this.error.name, ["exception.message"]: this.error.message }, this.includeIfDefined("exception.stack_trace", this.error.stack)))), (parsedTraceparentHeader && {
75
+ ["trace.id"]: parsedTraceparentHeader.traceId,
76
+ ["span.id"]: parsedTraceparentHeader.spanId,
77
+ })), (this.hasDroppedTraceparent && {
78
+ ["request.trace_context_hint"]: 'invalid_header',
79
+ })), this.includeIfTrue("dt.support.api.has_dropped_custom_properties", this.hasDroppedCustomProperties)), this.includeIfTrue("dt.support.has_nfn_values", this.hasNfnValues));
80
+ }
81
+ hasValidMandatoryAttriutes() {
82
+ let isValid = true;
83
+ if (this.isInvalidUrl(this.url)) {
84
+ logger.debug(`HttpRequestEventBuilder: dropped event since given URL is malformed: ${this.url}`);
85
+ isValid = false;
86
+ }
87
+ if (!HttpRequestEventBuilder.allowedRequestMethods.includes(this.requestMethod)) {
88
+ logger.debug(`HttpRequestEventBuilder: dropped event since given Request Method is invalid: ${this.requestMethod}`);
89
+ isValid = false;
90
+ }
91
+ return isValid;
92
+ }
93
+ isInvalidUrl(url) {
94
+ try {
95
+ if (!url.match(/^(https?):\/\/[^\s/$.?#-][^\s]*$/i)) {
96
+ return true;
97
+ }
98
+ new URL(url);
99
+ return false;
100
+ }
101
+ catch (_a) {
102
+ return true;
103
+ }
104
+ }
105
+ sanitizeStatusCode() {
106
+ if (this.statusCode == undefined && this.error === undefined) {
107
+ this.statusCode = 0;
108
+ }
109
+ if (this.statusCode && this.statusCode < 0) {
110
+ logger.debug('HttpRequestEventBuilder: overriding invalid Status Code with 0');
111
+ this.statusCode = 0;
112
+ this.hasDroppedCustomProperties = true;
113
+ }
114
+ }
115
+ sanitizeReasonPhrase() {
116
+ if (this.reasonPhrase &&
117
+ this.reasonPhrase.length >
118
+ HttpRequestEventBuilder.maxReasonPhraseLength) {
119
+ logger.debug(`HttpRequestEventBuilder: trimming too long Reason Phrase to a length of ${HttpRequestEventBuilder.maxReasonPhraseLength}`);
120
+ this.reasonPhrase = this.reasonPhrase.slice(0, HttpRequestEventBuilder.maxReasonPhraseLength);
121
+ }
122
+ }
123
+ sanitizeDuration() {
124
+ this.hasNfnValues || (this.hasNfnValues = !Number.isFinite(this.duration));
125
+ if (!Number.isFinite(this.duration) || this.duration < 0) {
126
+ logger.debug('HttpRequestEventBuilder: overriding invalid Duration with 0');
127
+ this.duration = 0;
128
+ this.hasDroppedCustomProperties = true;
129
+ }
130
+ }
131
+ sanitizeBytesSent() {
132
+ if (this.bytesSent && this.bytesSent < 0) {
133
+ logger.debug(`HttpRequestEventBuilder: dropping invalid value for Bytes Sent: ${this.bytesSent}`);
134
+ this.bytesSent = undefined;
135
+ this.hasDroppedCustomProperties = true;
136
+ }
137
+ }
138
+ sanitizeBytesReceived() {
139
+ if (this.bytesReceived && this.bytesReceived < 0) {
140
+ logger.debug(`HttpRequestEventBuilder: dropping invalid value for Bytes Received: ${this.bytesReceived}`);
141
+ this.bytesReceived = undefined;
142
+ this.hasDroppedCustomProperties = true;
143
+ }
144
+ }
145
+ isStatusCodeError() {
146
+ return (this.statusCode && 400 <= this.statusCode && this.statusCode <= 599);
147
+ }
148
+ isEventPropertyKey([key, _]) {
149
+ if (key.startsWith("event_properties")) {
150
+ return true;
151
+ }
152
+ logger.debug(`HttpRequestEventBuilder: dropped event property '$key' as it did not start with prefix ${"event_properties"}`);
153
+ this.hasDroppedCustomProperties = true;
154
+ return false;
155
+ }
156
+ includeIfDefined(key, value) {
157
+ return value !== undefined ? { [key]: value } : {};
158
+ }
159
+ includeIfTrue(key, value) {
160
+ return value === true ? { [key]: value } : {};
161
+ }
162
+ parseTraceparent(header) {
163
+ const traceparentRegex = /^00-([0-9a-f]{32})-([0-9a-f]{16})-0[01]$/;
164
+ const match = header.match(traceparentRegex);
165
+ if (!match) {
166
+ logger.debug("The provided traceparent header does not match the format: '00-<trace-id-32-HEXDIG>-<parent-id-16-HEXDIG>-<trace-flags-2-HEXDIG>'");
167
+ return null;
168
+ }
169
+ const [, traceId, spanId] = match;
170
+ if (this.allZeros(traceId)) {
171
+ logger.debug('Trace ID in traceparent header must not be all zeros');
172
+ return null;
173
+ }
174
+ if (this.allZeros(spanId)) {
175
+ logger.debug('Parent ID in traceparent header must not be all zeros');
176
+ return null;
177
+ }
178
+ return { traceId, spanId };
179
+ }
180
+ allZeros(str) {
181
+ return /^0*$/.test(str);
182
+ }
183
+ }
184
+ exports.default = HttpRequestEventBuilder;
185
+ HttpRequestEventBuilder.allowedRequestMethods = [
186
+ 'GET',
187
+ 'HEAD',
188
+ 'POST',
189
+ 'PUT',
190
+ 'DELETE',
191
+ 'CONNECT',
192
+ 'OPTIONS',
193
+ 'TRACE',
194
+ 'PATCH',
195
+ ];
196
+ HttpRequestEventBuilder.maxReasonPhraseLength = 5000;
@@ -8,7 +8,7 @@ const ASCII_CODE_A = 65;
8
8
  const NUMERICAL_CHARACTER_COUNT = 10;
9
9
  const OFFSET_A = ASCII_CODE_A - NUMERICAL_CHARACTER_COUNT;
10
10
  const createViewInfo = (name) => ({
11
- ["view.id"]: createRandomHexString(VIEW_ID_LENGTH),
11
+ ["view.instance_id"]: createRandomHexString(VIEW_ID_LENGTH),
12
12
  ["view.name"]: name,
13
13
  });
14
14
  exports.createViewInfo = createViewInfo;