UNPKG

@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
"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', ];