@dynatrace/react-native-plugin
Version:
This plugin gives you the ability to use the Dynatrace Mobile agent in your react native application.
175 lines (174 loc) • 7.77 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ConsoleLogger_1 = require("../../core/logging/ConsoleLogger");
const TimestampProvider_1 = require("../provider/TimestampProvider");
const SendEventValidation_1 = require("./modifier/SendEventValidation");
const EventModifierUtil_1 = require("./modifier/EventModifierUtil");
const EventBuilderUtil_1 = require("./EventBuilderUtil");
const logger = new ConsoleLogger_1.ConsoleLogger('HttpRequestEventData');
class HttpRequestEventData {
constructor(url, requestMethod) {
this.url = url;
this.requestMethod = requestMethod;
this.duration = 0;
this.rawEventProperties = {};
this.hasDroppedProperties = false;
this.triedToOverwriteDuration = false;
this.requestMethod =
requestMethod.toUpperCase();
}
withDuration(duration) {
this.duration = duration;
this.triedToOverwriteDuration = true;
return this;
}
withStatusCode(statusCode) {
this.statusCode = statusCode;
return this;
}
withReasonPhrase(reasonPhrase) {
this.reasonPhrase = reasonPhrase;
return this;
}
withError(error) {
this.error = error;
return this;
}
withBytesSent(bytesSent) {
this.bytesSent = bytesSent;
return this;
}
withBytesReceived(bytesReceived) {
this.bytesReceived = bytesReceived;
return this;
}
withTraceparentHeader(traceparentHeader) {
this.traceparentHeader = traceparentHeader;
return this;
}
addEventProperty(key, value) {
this.rawEventProperties[key] = value;
return this;
}
toJSON() {
if (!this.hasValidMandatoryAttributes()) {
logger.debug('toJSON(): Dropped invalid event');
return null;
}
const sanitizedDuration = this.sanitizeDuration();
const sanitizedStatusCode = this.sanitizeStatusCode();
const sanitizedBytesReceived = this.sanitizeBytesReceived();
const sanitizedBytesSent = this.sanitizeBytesSent();
const parsedTraceparentHeader = this.traceparentHeader &&
this.parseTraceparent(this.traceparentHeader);
const traceContextHint = !this.traceparentHeader
? "api_unused"
: parsedTraceparentHeader
? "api_set"
: "invalid";
const hasFailedRequest = this.isStatusCodeError() || !!this.error;
const sanitizedEventProperties = SendEventValidation_1.SendEventValidation.modifyEvent(this.rawEventProperties);
(0, EventModifierUtil_1.flagEventProperties)(sanitizedEventProperties);
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({}, sanitizedEventProperties), { ["url.full"]: this.url, ["network.protocol.name"]: 'http', ["http.request.method"]: this.requestMethod, ["request.trace_context_hint"]: traceContextHint, ["duration"]: sanitizedDuration, ["start_time"]: TimestampProvider_1.defaultTimestampProvider.getCurrentTimestamp() -
sanitizedDuration }), (this.triedToOverwriteDuration && {
["dt.support.api.overridden_fields"]: [
'duration',
],
})), (0, EventBuilderUtil_1.includeIfDefined)("http.response.status_code", sanitizedStatusCode)), (0, EventBuilderUtil_1.includeIfDefined)("http.response.reason_phrase", this.reasonPhrase)), (0, EventBuilderUtil_1.includeIfDefined)("request.bytes_sent", sanitizedBytesSent)), (0, EventBuilderUtil_1.includeIfDefined)("request.bytes_received", sanitizedBytesReceived)), { ["characteristics.has_request"]: true, ["characteristics.is_api_reported"]: true }), (0, EventBuilderUtil_1.includeIfTrue)("characteristics.has_failed_request", hasFailedRequest)), (0, EventBuilderUtil_1.includeIfTrue)("characteristics.has_error", hasFailedRequest)), (this.error !== undefined && Object.assign({ ["characteristics.has_exception"]: true, ["exception.type"]: this.error.name, ["exception.message"]: this.error.message }, (0, EventBuilderUtil_1.includeIfDefined)("exception.stack_trace", this.error.stack)))), (parsedTraceparentHeader && {
["trace.id"]: parsedTraceparentHeader.traceId,
["span.id"]: parsedTraceparentHeader.spanId,
})), (0, EventBuilderUtil_1.includeIfTrue)("dt.support.api.has_dropped_properties", this.hasDroppedProperties));
}
hasValidMandatoryAttributes() {
let isValid = true;
if (this.isInvalidUrl(this.url)) {
logger.debug(`hasValidMandatoryAttributes(): dropped event since given URL is malformed: ${this.url}`);
isValid = false;
}
if (!HttpRequestEventData.allowedRequestMethods.includes(this.requestMethod)) {
logger.debug(`hasValidMandatoryAttributes(): dropped event since given Request Method is invalid: ${this.requestMethod}`);
isValid = false;
}
return isValid;
}
isInvalidUrl(url) {
try {
if (!url.match(/^(https?):\/\/[^\s/$.?#-][^\s]*$/i)) {
return true;
}
new URL(url);
return false;
}
catch (_a) {
return true;
}
}
sanitizeStatusCode() {
if (this.statusCode !== undefined && this.statusCode < 0) {
logger.debug('HttpRequestEventData: dropping invalid Status Code');
this.hasDroppedProperties = true;
return undefined;
}
return this.statusCode;
}
sanitizeDuration() {
if (!Number.isFinite(this.duration) || this.duration < 0) {
logger.debug('HttpRequestEventData: overriding invalid Duration with 0');
this.hasDroppedProperties = true;
return 0;
}
return this.duration;
}
sanitizeBytesSent() {
if (this.bytesSent && this.bytesSent < 0) {
logger.debug(`HttpRequestEventData: dropping invalid value for Bytes Sent: ${this.bytesSent}`);
this.hasDroppedProperties = true;
return undefined;
}
return this.bytesSent;
}
sanitizeBytesReceived() {
if (this.bytesReceived && this.bytesReceived < 0) {
logger.debug(`HttpRequestEventData: dropping invalid value for Bytes Received: ${this.bytesReceived}`);
this.hasDroppedProperties = true;
return undefined;
}
return this.bytesReceived;
}
isStatusCodeError() {
return (this.statusCode && 400 <= this.statusCode && this.statusCode <= 599);
}
parseTraceparent(header) {
const traceparentRegex = /^00-([0-9a-f]{32})-([0-9a-f]{16})-0[01]$/;
const match = header.match(traceparentRegex);
if (!match) {
logger.debug("The provided traceparent header does not match the format: '00-<trace-id-32-HEXDIG>-<parent-id-16-HEXDIG>-<trace-flags-2-HEXDIG>'");
return null;
}
const [, traceId, spanId] = match;
if (this.allZeros(traceId)) {
logger.debug('Trace ID in traceparent header must not be all zeros');
return null;
}
if (this.allZeros(spanId)) {
logger.debug('Parent ID in traceparent header must not be all zeros');
return null;
}
return { traceId, spanId };
}
allZeros(str) {
return /^0*$/.test(str);
}
}
exports.default = HttpRequestEventData;
HttpRequestEventData.allowedRequestMethods = [
'GET',
'HEAD',
'POST',
'PUT',
'DELETE',
'CONNECT',
'OPTIONS',
'TRACE',
'PATCH',
];