@jufab/opentelemetry-angular-interceptor
Version:
@jufab/opentelemetry-angular-interceptor is an Angular Library to deploy [OpenTelemetry](https://opentelemetry.io/) in your Angular application
258 lines • 34.1 kB
JavaScript
import { Injectable, Inject, Optional } from '@angular/core';
import { PlatformLocation } from '@angular/common';
import * as api from '@opentelemetry/api';
import { SpanStatusCode, SpanKind } from '@opentelemetry/api';
import { WebTracerProvider, StackContextManager } from '@opentelemetry/sdk-trace-web';
import { SimpleSpanProcessor, ConsoleSpanExporter, BatchSpanProcessor, NoopSpanProcessor, AlwaysOnSampler, AlwaysOffSampler, TraceIdRatioBasedSampler, ParentBasedSampler } from '@opentelemetry/sdk-trace-base';
import { isUrlIgnored } from '@opentelemetry/core';
import { ATTR_USER_AGENT_ORIGINAL, ATTR_URL_QUERY, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_ERROR_TYPE, ATTR_SERVICE_NAME, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL, ATTR_URL_SCHEME, ATTR_SERVER_ADDRESS, ATTR_SERVER_PORT,
// ATTR_HTTP_REQUEST_HEADER,
//SEMATTRS_ERROR_TYPE
} from '@opentelemetry/semantic-conventions';
import { resourceFromAttributes } from '@opentelemetry/resources';
import { tap, finalize } from 'rxjs/operators';
import { OTEL_CONFIG, } from '../configuration/opentelemetry-config';
import infoLibrary from '../../version.json';
import { OTEL_EXPORTER } from '../services/exporter/exporter.interface';
import { OTEL_PROPAGATOR } from '../services/propagator/propagator.interface';
import { OTEL_LOGGER, OTEL_CUSTOM_SPAN } from '../configuration/opentelemetry-config';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common";
/**
* OpenTelemetryInterceptor class
*/
export class OpenTelemetryHttpInterceptor {
/**
* constructor
*
* @param config configuration
* @param exporterService service exporter injected
* @param propagatorService propagator injected
* @param logger define logger
* @param customSpan a customSpan interface to add attributes
* @param platformLocation encapsulates all calls to DOM APIs
*/
constructor(config, exporterService, propagatorService, logger, customSpan, platformLocation) {
this.config = config;
this.exporterService = exporterService;
this.propagatorService = propagatorService;
this.logger = logger;
this.customSpan = customSpan;
this.platformLocation = platformLocation;
/**
* Log or not body
*/
this.logBody = false;
this.tracer = new WebTracerProvider({
sampler: this.defineProbabilitySampler(this.convertStringToNumber(config.commonConfig.probabilitySampler)),
resource: this.loadResourceAttributes(this.config.commonConfig),
spanProcessors: this.insertOrNotSpanExporter()
});
this.contextManager = new StackContextManager();
this.tracer.register({
propagator: this.propagatorService.getPropagator(),
contextManager: this.contextManager
});
this.logBody = config.commonConfig.logBody;
api.diag.setLogger(logger, config.commonConfig.logLevel);
}
/**
* Overide method
* Interceptor from HttpInterceptor Angular
*
* @param request the current request
* @param next next
*/
intercept(request, next) {
if (isUrlIgnored(request.url, this.config.ignoreUrls?.urls)) {
return next.handle(request);
}
this.contextManager.disable(); //FIX - reinit contextManager for each http call
this.contextManager.enable();
const span = this.initSpan(request);
const tracedReq = this.injectContextAndHeader(request);
return next.handle(tracedReq).pipe(tap((event) => {
span.setAttributes({
[ATTR_HTTP_RESPONSE_STATUS_CODE]: event.status,
});
if (this.logBody && event.body != null) {
span.addEvent('response', { body: JSON.stringify(event.body) });
}
span.setStatus({
code: SpanStatusCode.UNSET
});
this.setCustomSpan(span, request, event);
}, (event) => {
span.setAttributes({
[ATTR_HTTP_RESPONSE_STATUS_CODE]: event.status,
[ATTR_ERROR_TYPE]: event.name,
});
span.recordException({
name: event.name,
message: event.message,
stack: event.error
});
span.setStatus({
code: SpanStatusCode.ERROR
});
this.setCustomSpan(span, request, event);
}), finalize(() => {
span.end();
this.contextManager.disable();
}));
}
/**
* Get current scheme, hostname and port
*/
getURL() {
return this.platformLocation.href;
}
/**
* Generate Resource Attributes
*/
loadResourceAttributes(commonConfig) {
return resourceFromAttributes({
[ATTR_SERVICE_NAME]: commonConfig?.serviceName,
...commonConfig?.resourceAttributes,
});
}
/**
* Initialise a span for a request intercepted
*
* @param request request
*/
initSpan(request) {
const urlRequest = (request.urlWithParams.startsWith('http')) ? new URL(request.urlWithParams) : new URL(this.getURL());
const span = this.tracer
.getTracer(infoLibrary.name, infoLibrary.version)
.startSpan(`${request.method.toUpperCase()}`, {
attributes: {
[ATTR_HTTP_REQUEST_METHOD]: request.method,
[ATTR_SERVER_ADDRESS]: urlRequest.host,
[ATTR_SERVER_PORT]: urlRequest.port,
[ATTR_URL_FULL]: request.urlWithParams,
[ATTR_URL_SCHEME]: urlRequest.protocol.replace(':', ''),
[ATTR_URL_QUERY]: urlRequest.search,
[ATTR_USER_AGENT_ORIGINAL]: window.navigator.userAgent
},
kind: SpanKind.CLIENT,
}, this.contextManager.active());
/*eslint no-underscore-dangle: ["error", { "allow": ["_currentContext"] }]*/
this.contextManager._currentContext = api.trace.setSpan(this.contextManager.active(), span);
return span;
}
/**
* Add header propagator in request and conserve original header
*
* @param request request
*/
injectContextAndHeader(request) {
const carrier = {};
api.propagation.inject(this.contextManager.active(), carrier, api.defaultTextMapSetter);
request.headers.keys().map(key => {
carrier[key] = request.headers.get(key);
});
return request.clone({
setHeaders: carrier,
});
}
/**
* Verify to insert or not a Span Exporter
*/
insertOrNotSpanExporter() {
if (this.exporterService.getExporter() !== undefined) {
return Array.of(this.insertSpanProcessorProductionMode(), this.insertConsoleSpanExporter());
}
else {
return Array.of(new NoopSpanProcessor());
}
}
/**
* Insert in tracer the console span if config is true
*/
insertConsoleSpanExporter() {
if (this.config.commonConfig.console) {
return new SimpleSpanProcessor(new ConsoleSpanExporter());
}
}
/**
* Insert BatchSpanProcessor in production mode
* SimpleSpanProcessor otherwise
*/
insertSpanProcessorProductionMode() {
const bufferConfig = {
maxExportBatchSize: this.convertStringToNumber(this.config.batchSpanProcessorConfig?.maxExportBatchSize),
scheduledDelayMillis: this.convertStringToNumber(this.config.batchSpanProcessorConfig?.scheduledDelayMillis),
exportTimeoutMillis: this.convertStringToNumber(this.config.batchSpanProcessorConfig?.exportTimeoutMillis),
maxQueueSize: this.convertStringToNumber(this.config.batchSpanProcessorConfig?.maxQueueSize)
};
return this.config.commonConfig.production
? new BatchSpanProcessor(this.exporterService.getExporter(), bufferConfig)
: new SimpleSpanProcessor(this.exporterService.getExporter());
}
/**
* define the Probability Sampler
* By Default, it's always (or 1)
*
* @param sampleConfig the sample configuration
*/
defineProbabilitySampler(sampleConfig) {
if (sampleConfig >= 1) {
return new ParentBasedSampler({ root: new AlwaysOnSampler() });
}
else if (sampleConfig <= 0 || sampleConfig === undefined) {
return new ParentBasedSampler({ root: new AlwaysOffSampler() });
}
else {
return new ParentBasedSampler({ root: new TraceIdRatioBasedSampler(sampleConfig) });
}
}
/**
* convert String to Number (or undefined)
*
* @param value
* @returns number or undefined
*/
convertStringToNumber(value) {
return value !== undefined ? Number(value) : undefined;
}
/**
* Set custom attributes in span with a CustomSpan
*
* @param span
* @param request
* @param response
* @returns Span
*/
setCustomSpan(span, request, response) {
return this.customSpan != null ? this.customSpan.add(span, request, response) : span;
}
}
OpenTelemetryHttpInterceptor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: OpenTelemetryHttpInterceptor, deps: [{ token: OTEL_CONFIG }, { token: OTEL_EXPORTER }, { token: OTEL_PROPAGATOR }, { token: OTEL_LOGGER, optional: true }, { token: OTEL_CUSTOM_SPAN, optional: true }, { token: i1.PlatformLocation }], target: i0.ɵɵFactoryTarget.Injectable });
OpenTelemetryHttpInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: OpenTelemetryHttpInterceptor, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: OpenTelemetryHttpInterceptor, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [OTEL_CONFIG]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [OTEL_EXPORTER]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [OTEL_PROPAGATOR]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [OTEL_LOGGER]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [OTEL_CUSTOM_SPAN]
}] }, { type: i1.PlatformLocation }]; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbnRlbGVtZXRyeS1odHRwLmludGVyY2VwdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvb3BlbnRlbGVtZXRyeS1pbnRlcmNlcHRvci9zcmMvbGliL2ludGVyY2VwdG9yL29wZW50ZWxlbWV0cnktaHR0cC5pbnRlcmNlcHRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFTN0QsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFbkQsT0FBTyxLQUFLLEdBQUcsTUFBTSxvQkFBb0IsQ0FBQztBQUMxQyxPQUFPLEVBQVEsY0FBYyxFQUFjLFFBQVEsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2hGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ3RGLE9BQU8sRUFDTCxtQkFBbUIsRUFDbkIsbUJBQW1CLEVBQ25CLGtCQUFrQixFQUNsQixpQkFBaUIsRUFDakIsZUFBZSxFQUNmLGdCQUFnQixFQUNoQix3QkFBd0IsRUFDeEIsa0JBQWtCLEVBR25CLE1BQU0sK0JBQStCLENBQUM7QUFDdkMsT0FBTyxFQUNMLFlBQVksRUFDYixNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sRUFDTCx3QkFBd0IsRUFFeEIsY0FBYyxFQUNkLDhCQUE4QixFQUM5QixlQUFlLEVBQ2YsaUJBQWlCLEVBQ2pCLHdCQUF3QixFQUN4QixhQUFhLEVBQ2IsZUFBZSxFQUNmLG1CQUFtQixFQUNuQixnQkFBZ0I7QUFDaEIsNEJBQTRCO0FBQzVCLHFCQUFxQjtFQUN0QixNQUFNLHFDQUFxQyxDQUFDO0FBQzdDLE9BQU8sRUFBWSxzQkFBc0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzVFLE9BQU8sRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFF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amVjdCwgT3B0aW9uYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIEh0dHBSZXF1ZXN0LFxuICBIdHRwSGFuZGxlcixcbiAgSHR0cEV2ZW50LFxuICBIdHRwSW50ZXJjZXB0b3IsXG4gIEh0dHBSZXNwb25zZSxcbiAgSHR0cEVycm9yUmVzcG9uc2Vcbn0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgUGxhdGZvcm1Mb2NhdGlvbiB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgKiBhcyBhcGkgZnJvbSAnQG9wZW50ZWxlbWV0cnkvYXBpJztcbmltcG9ydCB7IFNwYW4sIFNwYW5TdGF0dXNDb2RlLCBEaWFnTG9nZ2VyLCBTcGFuS2luZCB9IGZyb20gJ0BvcGVudGVsZW1ldHJ5L2FwaSc7XG5pbXBvcnQgeyBXZWJUcmFjZXJQcm92aWRlciwgU3RhY2tDb250ZXh0TWFuYWdlciB9IGZyb20gJ0BvcGVudGVsZW1ldHJ5L3Nkay10cmFjZS13ZWInO1xuaW1wb3J0IHtcbiAgU2ltcGxlU3BhblByb2Nlc3NvcixcbiAgQ29uc29sZVNwYW5FeHBvcnRlcixcbiAgQmF0Y2hTcGFuUHJvY2Vzc29yLFxuICBOb29wU3BhblByb2Nlc3NvcixcbiAgQWx3YXlzT25TYW1wbGVyLFxuICBBbHdheXNPZmZTYW1wbGVyLFxuICBUcmFjZUlkUmF0aW9CYXNlZFNhbXBsZXIsXG4gIFBhcmVudEJhc2VkU2FtcGxlcixcbiAgU2FtcGxlcixcbiAgQnVmZmVyQ29uZmlnXG59IGZyb20gJ0BvcGVudGVsZW1ldHJ5L3Nkay10cmFjZS1iYXNlJztcbmltcG9ydCB7XG4gIGlzVXJsSWdub3JlZFxufSBmcm9tICdAb3BlbnRlbGVtZXRyeS9jb3JlJztcbmltcG9ydCB7XG4gIEFUVFJfVVNFUl9BR0VOVF9PUklHSU5BTCxcbiAgQVRUUl9VUkxfUEFUSCxcbiAgQVRUUl9VUkxfUVVFUlksXG4gIEFUVFJfSFRUUF9SRVNQT05TRV9TVEFUVVNfQ09ERSxcbiAgQVRUUl9FUlJPUl9UWVBFLFxuICBBVFRSX1NFUlZJQ0VfTkFNRSxcbiAgQVRUUl9IVFRQX1JFUVVFU1RfTUVUSE9ELFxuICBBVFRSX1VSTF9GVUxMLFxuICBBVFRSX1VSTF9TQ0hFTUUsXG4gIEFUVFJfU0VSVkVSX0FERFJFU1MsXG4gIEFUVFJfU0VSVkVSX1BPUlQsXG4gIC8vIEFUVFJfSFRUUF9SRVFVRVNUX0hFQURFUixcbiAgLy9TRU1BVFRSU19FUlJPUl9UWVBFXG59IGZyb20gJ0BvcGVudGVsZW1ldHJ5L3NlbWFudGljLWNvbnZlbnRpb25zJztcbmltcG9ydCB7IFJlc291cmNlLCByZXNvdXJjZUZyb21BdHRyaWJ1dGVzIH0gZnJvbSAnQG9wZW50ZWxlbWV0cnkvcmVzb3VyY2VzJztcbmltcG9ydCB7IHRhcCwgZmluYWxpemUgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQge1xuICBDb21tb25Db2xsZWN0b3JDb25maWcsXG4gIE9wZW5UZWxlbWV0cnlDb25maWcsXG4gIE9URUxfQ09ORklHLFxufSBmcm9tICcuLi9jb25maWd1cmF0aW9uL29wZW50ZWxlbWV0cnktY29uZmlnJztcbmltcG9ydCBpbmZvTGlicmFyeSBmcm9tICcuLi8uLi92ZXJzaW9uLmpzb24nO1xuaW1wb3J0IHsgT1RFTF9FWFBPUlRFUiwgSUV4cG9ydGVyIH0gZnJvbSAnLi4vc2VydmljZXMvZXhwb3J0ZXIvZXhwb3J0ZXIuaW50ZXJmYWNlJztcbmltcG9ydCB7IE9URUxfUFJPUEFHQVRPUiwgSVByb3BhZ2F0b3IgfSBmcm9tICcuLi9zZXJ2aWNlcy9wcm9wYWdhdG9yL3Byb3BhZ2F0b3IuaW50ZXJmYWNlJztcbmltcG9ydCB7IE9URUxfTE9HR0VSLCBPVEVMX0NVU1RPTV9TUEFOIH0gZnJvbSAnLi4vY29uZmlndXJhdGlvbi9vcGVudGVsZW1ldHJ5LWNvbmZpZyc7XG5pbXBvcnQgeyBDdXN0b21TcGFuIH0gZnJvbSAnLi9jdXN0b20tc3Bhbi5pbnRlcmZhY2UnO1xuXG4vKipcbiAqIE9wZW5UZWxlbWV0cnlJbnRlcmNlcHRvciBjbGFzc1xuICovXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290Jyxcbn0pXG5leHBvcnQgY2xhc3MgT3BlblRlbGVtZXRyeUh0dHBJbnRlcmNlcHRvciBpbXBsZW1lbnRzIEh0dHBJbnRlcmNlcHRvciB7XG4gIC8qKlxuICAgKiB0cmFjZXJcbiAgICovXG4gIHRyYWNlcjogV2ViVHJhY2VyUHJvdmlkZXI7XG4gIC8qKlxuICAgKiBjb250ZXh0IG1hbmFnZXJcbiAgICovXG4gIGNvbnRleHRNYW5hZ2VyOiBTdGFja0NvbnRleHRNYW5hZ2VyO1xuICAvKipcbiAgICogTG9nIG9yIG5vdCBib2R5XG4gICAqL1xuICBsb2dCb2R5ID0gZmFsc2U7XG5cbiAgIC8qKlxuICAgKiBjb25zdHJ1Y3RvclxuICAgKlxuICAgKiBAcGFyYW0gY29uZmlnIGNvbmZpZ3VyYXRpb25cbiAgICogQHBhcmFtIGV4cG9ydGVyU2VydmljZSBzZXJ2aWNlIGV4cG9ydGVyIGluamVjdGVkXG4gICAqIEBwYXJhbSBwcm9wYWdhdG9yU2VydmljZSBwcm9wYWdhdG9yIGluamVjdGVkXG4gICAqIEBwYXJhbSBsb2dnZXIgZGVmaW5lIGxvZ2dlclxuICAgKiBAcGFyYW0gY3VzdG9tU3BhbiBhIGN1c3RvbVNwYW4gaW50ZXJmYWNlIHRvIGFkZCBhdHRyaWJ1dGVzXG4gICAqIEBwYXJhbSBwbGF0Zm9ybUxvY2F0aW9uIGVuY2Fwc3VsYXRlcyBhbGwgY2FsbHMgdG8gRE9NIEFQSXNcbiAgICovXG4gICBjb25zdHJ1Y3RvcihcbiAgICBASW5qZWN0KE9URUxfQ09ORklHKSBwcml2YXRlIGNvbmZpZzogT3BlblRlbGVtZXRyeUNvbmZpZyxcbiAgICBASW5qZWN0KE9URUxfRVhQT1JURVIpXG4gICAgcHJpdmF0ZSBleHBvcnRlclNlcnZpY2U6IElFeHBvcnRlcixcbiAgICBASW5qZWN0KE9URUxfUFJPUEFHQVRPUilcbiAgICBwcml2YXRlIHByb3BhZ2F0b3JTZXJ2aWNlOiBJUHJvcGFnYXRvcixcbiAgICBAT3B0aW9uYWwoKSBASW5qZWN0KE9URUxfTE9HR0VSKVxuICAgIHByaXZhdGUgbG9nZ2VyOiBEaWFnTG9nZ2VyLFxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoT1RFTF9DVVNUT01fU1BBTilcbiAgICBwcml2YXRlIGN1c3RvbVNwYW46IEN1c3RvbVNwYW4sXG4gICAgcHJpdmF0ZSBwbGF0Zm9ybUxvY2F0aW9uOiBQbGF0Zm9ybUxvY2F0aW9uXG4gICkge1xuICAgIHRoaXMudHJhY2VyID0gbmV3IFdlYlRyYWNlclByb3ZpZGVyKHtcbiAgICAgIHNhbXBsZXI6IHRoaXMuZGVmaW5lUHJvYmFiaWxpdHlTYW1wbGVyKHRoaXMuY29udmVydFN0cmluZ1RvTnVtYmVyKGNvbmZpZy5jb21tb25Db25maWcucHJvYmFiaWxpdHlTYW1wbGVyKSksXG4gICAgICByZXNvdXJjZTogdGhpcy5sb2FkUmVzb3VyY2VBdHRyaWJ1dGVzKHRoaXMuY29uZmlnLmNvbW1vbkNvbmZpZyksXG4gICAgICBzcGFuUHJvY2Vzc29yczogdGhpcy5pbnNlcnRPck5vdFNwYW5FeHBvcnRlcigpXG4gICAgfSk7XG4gICAgdGhpcy5jb250ZXh0TWFuYWdlciA9IG5ldyBTdGFja0NvbnRleHRNYW5hZ2VyKCk7XG4gICAgdGhpcy50cmFjZXIucmVnaXN0ZXIoe1xuICAgICAgcHJvcGFnYXRvcjogdGhpcy5wcm9wYWdhdG9yU2VydmljZS5nZXRQcm9wYWdhdG9yKCksXG4gICAgICBjb250ZXh0TWFuYWdlcjogdGhpcy5jb250ZXh0TWFuYWdlclxuICAgIH0pO1xuICAgIHRoaXMubG9nQm9keSA9IGNvbmZpZy5jb21tb25Db25maWcubG9nQm9keTtcbiAgICBhcGkuZGlhZy5zZXRMb2dnZXIobG9nZ2VyLCBjb25maWcuY29tbW9uQ29uZmlnLmxvZ0xldmVsKTtcbiAgfVxuXG4gICAgLyoqXG4gICAqIE92ZXJpZGUgbWV0aG9kXG4gICAqIEludGVyY2VwdG9yIGZyb20gSHR0cEludGVyY2VwdG9yIEFuZ3VsYXJcbiAgICpcbiAgICogQHBhcmFtIHJlcXVlc3QgdGhlIGN1cnJlbnQgcmVxdWVzdFxuICAgKiBAcGFyYW0gbmV4dCBuZXh0XG4gICAqL1xuICAgIGludGVyY2VwdChcbiAgICAgIHJlcXVlc3Q6IEh0dHBSZXF1ZXN0PHVua25vd24+LFxuICAgICAgbmV4dDogSHR0cEhhbmRsZXJcbiAgICApOiBPYnNlcnZhYmxlPEh0dHBFdmVudDx1bmtub3duPj4ge1xuICAgICAgaWYgKGlzVXJsSWdub3JlZChyZXF1ZXN0LnVybCwgdGhpcy5jb25maWcuaWdub3JlVXJscz8udXJscykpIHtcbiAgICAgICAgcmV0dXJuIG5leHQuaGFuZGxlKHJlcXVlc3QpO1xuICAgICAgfVxuICAgICAgdGhpcy5jb250ZXh0TWFuYWdlci5kaXNhYmxlKCk7IC8vRklYIC0gcmVpbml0IGNvbnRleHRNYW5hZ2VyIGZvciBlYWNoIGh0dHAgY2FsbFxuICAgICAgdGhpcy5jb250ZXh0TWFuYWdlci5lbmFibGUoKTtcbiAgICAgIGNvbnN0IHNwYW46IFNwYW4gPSB0aGlzLmluaXRTcGFuKHJlcXVlc3QpO1xuICAgICAgY29uc3QgdHJhY2VkUmVxID0gdGhpcy5pbmplY3RDb250ZXh0QW5kSGVhZGVyKHJlcXVlc3QpO1xuICAgICAgcmV0dXJuIG5leHQuaGFuZGxlKHRyYWNlZFJlcSkucGlwZShcbiAgICAgICAgdGFwKFxuICAgICAgICAgIChldmVudDogSHR0cFJlc3BvbnNlPGFueT4pID0+IHtcbiAgICAgICAgICAgIHNwYW4uc2V0QXR0cmlidXRlcyhcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIFtBVFRSX0hUVFBfUkVTUE9OU0VfU1RBVFVTX0NPREVdOiBldmVudC5zdGF0dXMsXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBpZiAodGhpcy5sb2dCb2R5ICYmIGV2ZW50LmJvZHkgIT0gbnVsbCkge1xuICAgICAgICAgICAgICBzcGFuLmFkZEV2ZW50KCdyZXNwb25zZScsIHsgYm9keTogSlNPTi5zdHJpbmdpZnkoZXZlbnQuYm9keSkgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzcGFuLnNldFN0YXR1cyh7XG4gICAgICAgICAgICAgIGNvZGU6IFNwYW5TdGF0dXNDb2RlLlVOU0VUXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMuc2V0Q3VzdG9tU3BhbihzcGFuLCByZXF1ZXN0LCBldmVudCk7XG4gICAgICAgICAgfSxcbiAgICAgICAgICAoZXZlbnQ6IEh0dHBFcnJvclJlc3BvbnNlKSA9PiB7XG4gICAgICAgICAgICBzcGFuLnNldEF0dHJpYnV0ZXMoXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBbQVRUUl9IVFRQX1JFU1BPTlNFX1NUQVRVU19DT0RFXTogZXZlbnQuc3RhdHVzLFxuICAgICAgICAgICAgICAgIFtBVFRSX0VSUk9SX1RZUEVdIDogZXZlbnQubmFtZSxcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHNwYW4ucmVjb3JkRXhjZXB0aW9uKHtcbiAgICAgICAgICAgICAgbmFtZTogZXZlbnQubmFtZSxcbiAgICAgICAgICAgICAgbWVzc2FnZTogZXZlbnQubWVzc2FnZSxcbiAgICAgICAgICAgICAgc3RhY2s6IGV2ZW50LmVycm9yXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHNwYW4uc2V0U3RhdHVzKHtcbiAgICAgICAgICAgICAgY29kZTogU3BhblN0YXR1c0NvZGUuRVJST1JcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5zZXRDdXN0b21TcGFuKHNwYW4sIHJlcXVlc3QsIGV2ZW50KTtcbiAgICAgICAgICB9XG4gICAgICAgICksXG4gICAgICAgIGZpbmFsaXplKCgpID0+IHtcbiAgICAgICAgICBzcGFuLmVuZCgpO1xuICAgICAgICAgIHRoaXMuY29udGV4dE1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICB9KVxuICAgICAgKTtcbiAgICB9XG5cbiAgLyoqXG4gICAqIEdldCBjdXJyZW50IHNjaGVtZSwgaG9zdG5hbWUgYW5kIHBvcnRcbiAgICovXG4gIHByaXZhdGUgZ2V0VVJMKCkge1xuICAgIHJldHVybiB0aGlzLnBsYXRmb3JtTG9jYXRpb24uaHJlZjtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBSZXNvdXJjZSBBdHRyaWJ1dGVzXG4gICAqL1xuICBwcml2YXRlIGxvYWRSZXNvdXJjZUF0dHJpYnV0ZXMoXG4gICAgY29tbW9uQ29uZmlnOiBDb21tb25Db2xsZWN0b3JDb25maWdcbiAgKTogUmVzb3VyY2Uge1xuICAgIHJldHVybiByZXNvdXJjZUZyb21BdHRyaWJ1dGVzKHtcbiAgICAgIFtBVFRSX1NFUlZJQ0VfTkFNRV06IGNvbW1vbkNvbmZpZz8uc2VydmljZU5hbWUsXG4gICAgICAuLi5jb21tb25Db25maWc/LnJlc291cmNlQXR0cmlidXRlcyxcbiAgICB9KTtcbiAgfVxuICAvKipcbiAgICogSW5pdGlhbGlzZSBhIHNwYW4gZm9yIGEgcmVxdWVzdCBpbnRlcmNlcHRlZFxuICAgKlxuICAgKiBAcGFyYW0gcmVxdWVzdCByZXF1ZXN0XG4gICAqL1xuICBwcml2YXRlIGluaXRTcGFuKHJlcXVlc3Q6IEh0dHBSZXF1ZXN0PHVua25vd24+KTogU3BhbiB7XG4gICAgY29uc3QgdXJsUmVxdWVzdCA9IChyZXF1ZXN0LnVybFdpdGhQYXJhbXMuc3RhcnRzV2l0aCgnaHR0cCcpKSA/IG5ldyBVUkwocmVxdWVzdC51cmxXaXRoUGFyYW1zKSA6IG5ldyBVUkwodGhpcy5nZXRVUkwoKSk7XG4gICAgY29uc3Qgc3BhbiA9IHRoaXMudHJhY2VyXG4gICAgICAuZ2V0VHJhY2VyKGluZm9MaWJyYXJ5Lm5hbWUsIGluZm9MaWJyYXJ5LnZlcnNpb24pXG4gICAgICAuc3RhcnRTcGFuKFxuICAgICAgICBgJHtyZXF1ZXN0Lm1ldGhvZC50b1VwcGVyQ2FzZSgpfWAsXG4gICAgICAgIHtcbiAgICAgICAgICBhdHRyaWJ1dGVzOiB7XG4gICAgICAgICAgICBbQVRUUl9IVFRQX1JFUVVFU1RfTUVUSE9EXTogcmVxdWVzdC5tZXRob2QsXG4gICAgICAgICAgICBbQVRUUl9TRVJWRVJfQUREUkVTU106IHVybFJlcXVlc3QuaG9zdCxcbiAgICAgICAgICAgIFtBVFRSX1NFUlZFUl9QT1JUXTogdXJsUmVxdWVzdC5wb3J0LFxuICAgICAgICAgICAgW0FUVFJfVVJMX0ZVTExdOiByZXF1ZXN0LnVybFdpdGhQYXJhbXMsXG4gICAgICAgICAgICBbQVRUUl9VUkxfU0NIRU1FXTogdXJsUmVxdWVzdC5wcm90b2NvbC5yZXBsYWNlKCc6JywgJycpLFxuICAgICAgICAgICAgW0FUVFJfVVJMX1FVRVJZXTogdXJsUmVxdWVzdC5zZWFyY2gsXG4gICAgICAgICAgICBbQVRUUl9VU0VSX0FHRU5UX09SSUdJTkFMXTogd2luZG93Lm5hdmlnYXRvci51c2VyQWdlbnRcbiAgICAgICAgICB9LFxuICAgICAgICAgIGtpbmQ6IFNwYW5LaW5kLkNMSUVOVCxcbiAgICAgICAgfSxcbiAgICAgICAgdGhpcy5jb250ZXh0TWFuYWdlci5hY3RpdmUoKVxuICAgICAgKTtcbiAgICAvKmVzbGludCBuby11bmRlcnNjb3JlLWRhbmdsZTogW1wiZXJyb3JcIiwgeyBcImFsbG93XCI6IFtcIl9jdXJyZW50Q29udGV4dFwiXSB9XSovXG4gICAgdGhpcy5jb250ZXh0TWFuYWdlci5fY3VycmVudENvbnRleHQgPSBhcGkudHJhY2Uuc2V0U3BhbihcbiAgICAgIHRoaXMuY29udGV4dE1hbmFnZXIuYWN0aXZlKCksXG4gICAgICBzcGFuXG4gICAgKTtcbiAgICByZXR1cm4gc3BhbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGQgaGVhZGVyIHByb3BhZ2F0b3IgaW4gcmVxdWVzdCBhbmQgY29uc2VydmUgb3JpZ2luYWwgaGVhZGVyXG4gICAqXG4gICAqIEBwYXJhbSByZXF1ZXN0IHJlcXVlc3RcbiAgICovXG4gIHByaXZhdGUgaW5qZWN0Q29udGV4dEFuZEhlYWRlcihcbiAgICByZXF1ZXN0OiBIdHRwUmVxdWVzdDx1bmtub3duPlxuICApIHtcbiAgICBjb25zdCBjYXJyaWVyID0ge307XG4gICAgYXBpLnByb3BhZ2F0aW9uLmluamVjdChcbiAgICAgIHRoaXMuY29udGV4dE1hbmFnZXIuYWN0aXZlKCksXG4gICAgICBjYXJyaWVyLFxuICAgICAgYXBpLmRlZmF1bHRUZXh0TWFwU2V0dGVyXG4gICAgKTtcbiAgICByZXF1ZXN0LmhlYWRlcnMua2V5cygpLm1hcChrZXkgPT4ge1xuICAgICAgY2FycmllcltrZXldID0gcmVxdWVzdC5oZWFkZXJzLmdldChrZXkpO1xuICAgIH0pO1xuICAgIHJldHVybiByZXF1ZXN0LmNsb25lKHtcbiAgICAgIHNldEhlYWRlcnM6IGNhcnJpZXIsXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogVmVyaWZ5IHRvIGluc2VydCBvciBub3QgYSBTcGFuIEV4cG9ydGVyXG4gICAqL1xuICBwcml2YXRlIGluc2VydE9yTm90U3BhbkV4cG9ydGVyKCkge1xuXG4gICAgaWYgKHRoaXMuZXhwb3J0ZXJTZXJ2aWNlLmdldEV4cG9ydGVyKCkgIT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIEFycmF5Lm9mKHRoaXMuaW5zZXJ0U3BhblByb2Nlc3NvclByb2R1Y3Rpb25Nb2RlKCksIHRoaXMuaW5zZXJ0Q29uc29sZVNwYW5FeHBvcnRlcigpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIEFycmF5Lm9mKG5ldyBOb29wU3BhblByb2Nlc3NvcigpKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSW5zZXJ0IGluIHRyYWNlciB0aGUgY29uc29sZSBzcGFuIGlmIGNvbmZpZyBpcyB0cnVlXG4gICAqL1xuICBwcml2YXRlIGluc2VydENvbnNvbGVTcGFuRXhwb3J0ZXIoKSB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmNvbW1vbkNvbmZpZy5jb25zb2xlKSB7XG4gICAgICByZXR1cm4gbmV3IFNpbXBsZVNwYW5Qcm9jZXNzb3IobmV3IENvbnNvbGVTcGFuRXhwb3J0ZXIoKSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEluc2VydCBCYXRjaFNwYW5Qcm9jZXNzb3IgaW4gcHJvZHVjdGlvbiBtb2RlXG4gICAqIFNpbXBsZVNwYW5Qcm9jZXNzb3Igb3RoZXJ3aXNlXG4gICAqL1xuICBwcml2YXRlIGluc2VydFNwYW5Qcm9jZXNzb3JQcm9kdWN0aW9uTW9kZSgpIHtcbiAgICBjb25zdCBidWZmZXJDb25maWc6IEJ1ZmZlckNvbmZpZyA9IHtcbiAgICAgIG1heEV4cG9ydEJhdGNoU2l6ZTogdGhpcy5jb252ZXJ0U3RyaW5nVG9OdW1iZXIodGhpcy5jb25maWcuYmF0Y2hTcGFuUHJvY2Vzc29yQ29uZmlnPy5tYXhFeHBvcnRCYXRjaFNpemUpLFxuICAgICAgc2NoZWR1bGVkRGVsYXlNaWxsaXM6IHRoaXMuY29udmVydFN0cmluZ1RvTnVtYmVyKHRoaXMuY29uZmlnLmJhdGNoU3BhblByb2Nlc3NvckNvbmZpZz8uc2NoZWR1bGVkRGVsYXlNaWxsaXMpLFxuICAgICAgZXhwb3J0VGltZW91dE1pbGxpczogdGhpcy5jb252ZXJ0U3RyaW5nVG9OdW1iZXIodGhpcy5jb25maWcuYmF0Y2hTcGFuUHJvY2Vzc29yQ29uZmlnPy5leHBvcnRUaW1lb3V0TWlsbGlzKSxcbiAgICAgIG1heFF1ZXVlU2l6ZTogdGhpcy5jb252ZXJ0U3RyaW5nVG9OdW1iZXIodGhpcy5jb25maWcuYmF0Y2hTcGFuUHJvY2Vzc29yQ29uZmlnPy5tYXhRdWV1ZVNpemUpXG4gICAgfTtcbiAgICByZXR1cm4gdGhpcy5jb25maWcuY29tbW9uQ29uZmlnLnByb2R1Y3Rpb25cbiAgICAgICAgPyBuZXcgQmF0Y2hTcGFuUHJvY2Vzc29yKHRoaXMuZXhwb3J0ZXJTZXJ2aWNlLmdldEV4cG9ydGVyKCksIGJ1ZmZlckNvbmZpZylcbiAgICAgICAgOiBuZXcgU2ltcGxlU3BhblByb2Nlc3Nvcih0aGlzLmV4cG9ydGVyU2VydmljZS5nZXRFeHBvcnRlcigpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBkZWZpbmUgdGhlIFByb2JhYmlsaXR5IFNhbXBsZXJcbiAgICogQnkgRGVmYXVsdCwgaXQncyBhbHdheXMgKG9yIDEpXG4gICAqXG4gICAqIEBwYXJhbSBzYW1wbGVDb25maWcgdGhlIHNhbXBsZSBjb25maWd1cmF0aW9uXG4gICAqL1xuICBwcml2YXRlIGRlZmluZVByb2JhYmlsaXR5U2FtcGxlcihzYW1wbGVDb25maWc6IG51bWJlcik6IFNhbXBsZXIge1xuICAgIGlmIChzYW1wbGVDb25maWcgPj0gMSkge1xuICAgICAgcmV0dXJuIG5ldyBQYXJlbnRCYXNlZFNhbXBsZXIoeyByb290OiBuZXcgQWx3YXlzT25TYW1wbGVyKCkgfSk7XG4gICAgfVxuICAgIGVsc2UgaWYgKHNhbXBsZUNvbmZpZyA8PSAwIHx8IHNhbXBsZUNvbmZpZyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gbmV3IFBhcmVudEJhc2VkU2FtcGxlcih7IHJvb3Q6IG5ldyBBbHdheXNPZmZTYW1wbGVyKCkgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBuZXcgUGFyZW50QmFzZWRTYW1wbGVyKHsgcm9vdDogbmV3IFRyYWNlSWRSYXRpb0Jhc2VkU2FtcGxlcihzYW1wbGVDb25maWcpIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBjb252ZXJ0IFN0cmluZyB0byBOdW1iZXIgKG9yIHVuZGVmaW5lZClcbiAgICpcbiAgICogQHBhcmFtIHZhbHVlXG4gICAqIEByZXR1cm5zIG51bWJlciBvciB1bmRlZmluZWRcbiAgICovXG4gIHByaXZhdGUgY29udmVydFN0cmluZ1RvTnVtYmVyKHZhbHVlOiBzdHJpbmcpOiBudW1iZXIge1xuICAgIHJldHVybiB2YWx1ZSAhPT0gdW5kZWZpbmVkID8gTnVtYmVyKHZhbHVlKSA6IHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgY3VzdG9tIGF0dHJpYnV0ZXMgaW4gc3BhbiB3aXRoIGEgQ3VzdG9tU3BhblxuICAgKlxuICAgKiBAcGFyYW0gc3BhblxuICAgKiBAcGFyYW0gcmVxdWVzdFxuICAgKiBAcGFyYW0gcmVzcG9uc2VcbiAgICogQHJldHVybnMgU3BhblxuICAgKi9cbiAgcHJpdmF0ZSBzZXRDdXN0b21TcGFuKHNwYW46IFNwYW4sIHJlcXVlc3Q6IEh0dHBSZXF1ZXN0PHVua25vd24+LCByZXNwb25zZTogSHR0cFJlc3BvbnNlPHVua25vd24+IHwgSHR0cEVycm9yUmVzcG9uc2UpOiBTcGFuIHtcbiAgICByZXR1cm4gdGhpcy5jdXN0b21TcGFuICE9IG51bGwgPyB0aGlzLmN1c3RvbVNwYW4uYWRkKHNwYW4sIHJlcXVlc3QsIHJlc3BvbnNlKSA6IHNwYW47XG4gIH1cbn1cbiJdfQ==