breeze-client
Version:
Breeze data management for JavaScript clients
461 lines • 68.3 kB
JavaScript
import * as breeze from 'breeze-client';
import { appendQueryStringParameter } from './adapter-core';
let core = breeze.core;
/** @hidden */
export class DataServiceODataAdapter extends breeze.AbstractDataServiceAdapter {
relativeUrl;
// _catchNoConnectionError = abstractDsaProto._catchNoConnectionError;
// changeRequestInterceptor = abstractDsaProto.changeRequestInterceptor;
// _createChangeRequestInterceptor = abstractDsaProto._createChangeRequestInterceptor;
headers = { "DataServiceVersion": "2.0" };
constructor() {
super();
this.name = "OData";
}
static register(config) {
config = config || breeze.config;
config.registerAdapter("dataService", DataServiceODataAdapter);
return config.initializeAdapterInstance("dataService", "OData", true);
}
initialize() {
OData = core.requireLib("OData", "Needed to support remote OData services");
OData.jsonHandler.recognizeDates = true;
}
// Absolute URL is the default as of Breeze 1.5.5.
// To use relative URL (like pre-1.5.5), add adapterInstance.relativeUrl = true:
//
// let ds = breeze.config.initializeAdapterInstance("dataService", "webApiOData");
// ds.relativeUrl = true;
//
// To use custom url construction, add adapterInstance.relativeUrl = myfunction(dataService, url):
//
// let ds = breeze.config.initializeAdapterInstance("dataService", "webApiOData");
// ds.relativeUrl = function(dataService, url) {
// return somehowConvert(url);
// }
//
fetchMetadata(metadataStore, dataService) {
let serviceName = dataService.serviceName;
let url;
if (this.relativeUrl === true) {
url = dataService.qualifyUrl('$metadata');
}
else if (core.isFunction(this.relativeUrl)) {
url = this.relativeUrl(dataService, '$metadata');
}
else {
url = this.getAbsoluteUrl(dataService, '$metadata');
}
let mheaders = core.extend({}, this.headers);
mheaders.Accept = 'application/*; odata.metadata=full';
let promise = new Promise((resolve, reject) => {
// OData.read(url,
OData.read({
requestUri: url,
// headers: { "Accept": "application/json"}
headers: mheaders
}, function (data) {
// data.dataServices.schema is an array of schemas. with properties of
// entityContainer[], association[], entityType[], and namespace.
if (!data || !data.dataServices) {
let error = new Error("Metadata query failed for: " + url);
return reject(error);
}
let csdlMetadata = data.dataServices;
// might have been fetched by another query
if (!metadataStore.hasMetadataFor(serviceName)) {
try {
metadataStore.importMetadata(csdlMetadata);
}
catch (e) {
return reject(new Error("Metadata query failed for " + url + "; Unable to process returned metadata: " + e.message));
}
metadataStore.addDataService(dataService);
}
return resolve(csdlMetadata);
}, function (error) {
let err = createError(error, url);
err.message = "Metadata query failed for: " + url + "; " + (err.message || "");
return reject(err);
}, OData.metadataHandler);
});
return promise;
}
executeQuery(mappingContext) {
let url;
if (this.relativeUrl === true) {
url = mappingContext.getUrl();
}
else if (core.isFunction(this.relativeUrl)) {
url = this.relativeUrl(mappingContext.dataService, mappingContext.getUrl());
}
else {
url = this.getAbsoluteUrl(mappingContext.dataService, mappingContext.getUrl());
}
// Add query params if .withParameters was used
let query = mappingContext.query;
if (!core.isEmpty(query.parameters)) {
let paramString = toQueryString(query.parameters);
url = appendQueryStringParameter(url, paramString);
}
let promise = new Promise((resolve, reject) => {
OData.read({
requestUri: url,
headers: core.extend({}, this.headers)
}, function (data, response) {
let inlineCount;
if (data.__count) {
// OData can return data.__count as a string
inlineCount = parseInt(data.__count, 10);
}
// Odata returns different result structure when it returns multiple entities (data.results) vs single entity (data directly).
// @see http://www.odata.org/documentation/odata-version-2-0/json-format/#RepresentingCollectionsOfEntries
// and http://www.odata.org/documentation/odata-version-2-0/json-format/#RepresentingEntries
let results;
if (data.results) {
results = data.results;
}
else {
results = data;
}
return resolve({ results: results, inlineCount: inlineCount, httpResponse: response, query: query });
}, function (error) {
return reject(createError(error, url));
});
});
return promise;
}
saveChanges(odataSaveContext, saveBundle) {
let adapter = odataSaveContext.adapter = this;
let saveContext = odataSaveContext;
let url;
if (this.relativeUrl === true) {
saveContext.routePrefix = adapter.getRoutePrefix(saveContext.dataService);
url = saveContext.dataService.qualifyUrl("$batch");
}
else if (core.isFunction(adapter.relativeUrl)) {
saveContext.routePrefix = adapter.relativeUrl(saveContext.dataService, '');
url = saveContext.routePrefix + '$batch';
}
else {
saveContext.routePrefix = adapter.getAbsoluteUrl(saveContext.dataService, '');
url = saveContext.routePrefix + '$batch';
}
let requestData = createChangeRequests(saveContext, saveBundle);
let tempKeys = saveContext.tempKeys;
let contentKeys = saveContext.contentKeys;
let promise = new Promise((resolve, reject) => {
OData.request({
headers: core.extend({}, this.headers),
requestUri: url,
method: "POST",
data: requestData
}, function (data, response) {
let entities = [];
let keyMappings = [];
let saveResult = { entities: entities, keyMappings: keyMappings };
data.__batchResponses.forEach(function (br) {
br.__changeResponses.forEach(function (cr) {
let response = cr.response || cr;
let statusCode = response.statusCode;
if ((!statusCode) || statusCode >= 400) {
reject(createError(cr, url));
return;
}
let contentId = cr.headers["Content-ID"];
// Olingo sends different case of 'ID' for the header name.
if (!contentId) {
contentId = cr.headers["Content-Id"];
}
let rawEntity = cr.data;
if (rawEntity) {
let tempKey = tempKeys[contentId];
if (tempKey) {
let entityType = tempKey.entityType;
if (entityType.autoGeneratedKeyType !== breeze.AutoGeneratedKeyType.None) {
let tempValue = tempKey.values[0];
let realKey = entityType.getEntityKeyFromRawEntity(rawEntity, breeze.DataProperty.getRawValueFromServer);
let keyMapping = { entityTypeName: entityType.name, tempValue: tempValue, realValue: realKey.values[0] };
keyMappings.push(keyMapping);
}
}
entities.push(rawEntity);
}
else {
let origEntity = contentKeys[contentId];
entities.push(origEntity);
}
});
});
return resolve(saveResult);
}, function (err) {
return reject(createError(err, url));
}, OData.batchHandler);
});
return promise;
}
jsonResultsAdapter = new breeze.JsonResultsAdapter({
name: "OData_default",
visitNode: function (node, mappingContext, nodeContext) {
let result = {};
if (node == null)
return result;
let metadata = node.__metadata;
if (metadata != null) {
// TODO: may be able to make this more efficient by caching of the previous value.
let entityTypeName = breeze.MetadataStore.normalizeTypeName(metadata.type);
let et = entityTypeName && mappingContext.entityManager.metadataStore.getEntityType(entityTypeName, true);
// OData response doesn't distinguish a projection from a whole entity.
// We'll assume that whole-entity data would have at least as many properties (<=)
// as the EntityType has mapped properties on the basis that
// most projections remove properties rather than add them.
// If not, assume it's a projection and do NOT treat as an entity
if (et && et._mappedPropertiesCount <= Object.keys(node).length - 1) {
// if (et && et._mappedPropertiesCount === Object.keys(node).length - 1) { // OLD
result.entityType = et;
let uriKey = metadata.uri || metadata.id;
if (uriKey) {
// Strip baseUri to make uriKey a relative uri
// Todo: why is this necessary when absolute works for every OData source tested?
let re = new RegExp('^' + mappingContext.dataService.serviceName, 'i');
uriKey = uriKey.replace(re, '');
}
result.extraMetadata = {
uriKey: uriKey,
etag: metadata.etag
};
}
}
// OData v3 - projection arrays will be enclosed in a results array
if (node.results) {
result.node = node.results;
}
let propertyName = nodeContext.propertyName;
result.ignore = node.__deferred != null || propertyName === "__metadata" ||
// EntityKey properties can be produced by EDMX models
(propertyName === "EntityKey" && node.$type && core.stringStartsWith(node.$type, "System.Data"));
return result;
}
});
getAbsoluteUrl(dataService, url) {
let serviceName = dataService.qualifyUrl('');
// only prefix with serviceName if not already on the url
let base = (core.stringStartsWith(url, serviceName)) ? '' : serviceName;
// If no protocol, turn base into an absolute URI
if (window && serviceName.indexOf('//') < 0) {
// no protocol; make it absolute
base = window.location.protocol + '//' + window.location.host +
(core.stringStartsWith(serviceName, '/') ? '' : '/') +
base;
}
return base + url;
}
getRoutePrefix(dataService) {
// Get the routePrefix from a Web API OData service name.
// The routePrefix is presumed to be the pathname within the dataService.serviceName
// Examples of servicename -> routePrefix:
// 'http://localhost:55802/odata/' -> 'odata/'
// 'http://198.154.121.75/service/odata/' -> 'service/odata/'
let parser;
if (typeof document === 'object') { // browser
parser = document.createElement('a');
parser.href = dataService.serviceName;
}
else { // node
// TODO: how to best handle this
// assumes existence of node's url.parse method.
parser = url.parse(dataService.serviceName);
}
let prefix = parser.pathname;
if (prefix[0] === '/') {
prefix = prefix.substr(1);
} // drop leading '/' (all but IE)
if (prefix.substr(-1) !== '/') {
prefix += '/';
} // ensure trailing '/'
return prefix;
}
}
// crude serializer. Doesn't recurse
function toQueryString(obj) {
let parts = [];
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
parts.push(encodeURIComponent(i) + "=" + encodeURIComponent(obj[i]));
}
}
return parts.join("&");
}
/** Transform data properties while unwrapping entities */
function transformValue(prop, val) {
if (prop.isUnmapped)
return undefined;
if (prop.dataType === breeze.DataType.DateTimeOffset) {
// The datajs lib tries to treat client dateTimes that are defined as DateTimeOffset on the server differently
// from other dateTimes. This fix compensates before the save.
val = val && new Date(val.getTime() - (val.getTimezoneOffset() * 60000));
}
else if (prop.dataType === breeze.DataType.DateOnly) {
val = breeze.DataType.toDateOnlyString(val);
}
else if (prop.dataType.quoteJsonOData) {
val = val != null ? val.toString() : val;
}
return val;
}
function createChangeRequests(saveContext, saveBundle) {
let changeRequestInterceptor = saveContext.adapter._createChangeRequestInterceptor(saveContext, saveBundle);
let changeRequests = [];
let tempKeys = [];
let contentKeys = [];
let entityManager = saveContext.entityManager;
let helper = entityManager.helper;
let id = 0;
let routePrefix = saveContext.routePrefix;
saveBundle.entities.forEach(function (entity, index) {
let aspect = entity.entityAspect;
id = id + 1; // we are deliberately skipping id=0 because Content-ID = 0 seems to be ignored.
let request = { headers: { "Content-ID": id, "DataServiceVersion": "2.0" } };
contentKeys[id] = entity;
if (aspect.entityState.isAdded()) {
request.requestUri = routePrefix + entity.entityType.defaultResourceName;
request.method = "POST";
request.data = helper.unwrapInstance(entity, transformValue);
tempKeys[id] = aspect.getKey();
}
else if (aspect.entityState.isModified()) {
updateDeleteMergeRequest(request, aspect, routePrefix);
request.method = "MERGE";
request.data = helper.unwrapChangedValues(entity, entityManager.metadataStore, transformValue);
// should be a PATCH/MERGE
}
else if (aspect.entityState.isDeleted()) {
updateDeleteMergeRequest(request, aspect, routePrefix);
request.method = "DELETE";
}
else {
return;
}
request = changeRequestInterceptor.getRequest(request, entity, index);
changeRequests.push(request);
});
saveContext.contentKeys = contentKeys;
saveContext.tempKeys = tempKeys;
changeRequestInterceptor.done(changeRequests);
return {
__batchRequests: [
{
__changeRequests: changeRequests
}
]
};
}
function updateDeleteMergeRequest(request, aspect, routePrefix) {
let uriKey;
let extraMetadata = aspect.extraMetadata;
if (extraMetadata == null) {
uriKey = getUriKey(aspect);
aspect.extraMetadata = {
uriKey: uriKey
};
}
else {
uriKey = extraMetadata.uriKey;
if (extraMetadata.etag) {
request.headers["If-Match"] = extraMetadata.etag;
}
}
request.requestUri =
// use routePrefix if uriKey lacks protocol (i.e., relative uri)
uriKey.indexOf('//') > 0 ? uriKey : routePrefix + uriKey;
}
function getUriKey(aspect) {
let entityType = aspect.entity.entityType;
let resourceName = entityType.defaultResourceName;
let kps = entityType.keyProperties;
let uriKey = resourceName + "(";
if (kps.length === 1) {
uriKey = uriKey + fmtProperty(kps[0], aspect) + ")";
}
else {
let delim = "";
kps.forEach(function (kp) {
uriKey = uriKey + delim + kp.nameOnServer + "=" + fmtProperty(kp, aspect);
delim = ",";
});
uriKey = uriKey + ")";
}
return uriKey;
}
function fmtProperty(prop, aspect) {
return prop.dataType.fmtOData(aspect.getPropertyValue(prop.name));
}
function createError(error, url) {
// OData errors can have the message buried very deeply - and nonobviously
// this code is tricky so be careful changing the response.body parsing.
let result = new Error();
let response = error && error.response;
if (!response) {
// in case DataJS returns "No handler for this data"
result.message = error;
result.statusText = error;
return result;
}
result.message = response.statusText;
result.statusText = response.statusText;
result.status = response.statusCode;
// non std
if (url)
result.url = url;
result.body = response.body;
if (response.body) {
let nextErr;
try {
let body = JSON.parse(response.body);
result.body = body;
// OData v3 logic
if (body['odata.error']) {
body = body['odata.error'];
}
let msg = "";
do {
nextErr = body.error || body.innererror;
if (!nextErr)
msg = msg + getMessage(body);
nextErr = nextErr || body.internalexception;
body = nextErr || body;
} while (nextErr);
if (msg.length > 0) {
result.message = msg;
}
}
catch (e) {
}
}
breeze.AbstractDataServiceAdapter._catchNoConnectionError(result);
return result;
}
function getMessage(body) {
let msg = body.message || "";
return ((typeof (msg) === "string") ? msg : msg.value) + "; ";
}
breeze.config.registerAdapter("dataService", DataServiceODataAdapter);
let webApiODataCtor = function () {
this.name = "webApiOData";
};
breeze.core.extend(webApiODataCtor.prototype, DataServiceODataAdapter.prototype);
breeze.config.registerAdapter("dataService", webApiODataCtor);
// OData 4 adapter
let webApiOData4Ctor = function () {
this.name = "webApiOData4";
};
breeze.core.extend(webApiOData4Ctor.prototype, webApiODataCtor.prototype);
webApiOData4Ctor.prototype.initialize = function () {
// Aargh... they moved the cheese.
let datajs = core.requireLib("datajs", "Needed to support remote OData v4 services");
OData = datajs.V4.oData;
OData.json.jsonHandler.recognizeDates = true;
};
webApiOData4Ctor.prototype.headers = { "OData-Version": "4.0" };
breeze.config.registerAdapter("dataService", webApiOData4Ctor);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRhcHRlci1kYXRhLXNlcnZpY2Utb2RhdGEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYWRhcHRlci1kYXRhLXNlcnZpY2Utb2RhdGEudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE1BQU0sTUFBTSxlQUFlLENBQUM7QUFDeEMsT0FBTyxFQUFFLDBCQUEwQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFNUQsSUFBSSxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQztBQVl2QixjQUFjO0FBQ2QsTUFBTSxPQUFPLHVCQUF3QixTQUFRLE1BQU0sQ0FBQywwQkFBMEI7SUFDNUUsV0FBVyxDQUE4RDtJQUN6RSxzRUFBc0U7SUFDdEUsd0VBQXdFO0lBQ3hFLHNGQUFzRjtJQUN0RixPQUFPLEdBQUcsRUFBRSxvQkFBb0IsRUFBRSxLQUFLLEVBQUUsQ0FBQztJQUUxQztRQUNFLEtBQUssRUFBRSxDQUFDO1FBQ1IsSUFBSSxDQUFDLElBQUksR0FBRyxPQUFPLENBQUM7SUFDdEIsQ0FBQztJQUVELE1BQU0sQ0FBQyxRQUFRLENBQUMsTUFBNEI7UUFDMUMsTUFBTSxHQUFHLE1BQU0sSUFBSSxNQUFNLENBQUMsTUFBTSxDQUFDO1FBQ2pDLE1BQU0sQ0FBQyxlQUFlLENBQUMsYUFBYSxFQUFFLHVCQUF1QixDQUFDLENBQUM7UUFDL0QsT0FBTyxNQUFNLENBQUMseUJBQXlCLENBQUMsYUFBYSxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQTRCLENBQUM7SUFDbkcsQ0FBQztJQUVELFVBQVU7UUFDUixLQUFLLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUseUNBQXlDLENBQUMsQ0FBQztRQUM1RSxLQUFLLENBQUMsV0FBVyxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUM7SUFDMUMsQ0FBQztJQUdELG9EQUFvRDtJQUNwRCxnRkFBZ0Y7SUFDaEYsRUFBRTtJQUNGLHNGQUFzRjtJQUN0Riw4QkFBOEI7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QyxDQUFDO0FBQ0YsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLE9BQU8sR0FBRyxFQUFFLGVBQWUsRUFBRSxLQUFLLEVBQUUsQ0FBQztBQUNoRSxNQUFNLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxhQUFhLEVBQUUsZ0JBQXVCLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGJyZWV6ZSBmcm9tICdicmVlemUtY2xpZW50JztcclxuaW1wb3J0IHsgYXBwZW5kUXVlcnlTdHJpbmdQYXJhbWV0ZXIgfSBmcm9tICcuL2FkYXB0ZXItY29yZSc7XHJcblxyXG5sZXQgY29yZSA9IGJyZWV6ZS5jb3JlO1xyXG5cclxuZGVjbGFyZSB2YXIgd2luZG93OiBhbnk7XHJcbmRlY2xhcmUgdmFyIGRvY3VtZW50OiBhbnk7XHJcbmRlY2xhcmUgdmFyIHVybDogYW55OyAvLyBuZWVkZWQgdG8gYWNjZXNzIG5vZGUncyB1cmwucGFyc2VcclxuZGVjbGFyZSB2YXIgT0RhdGE6IGFueTtcclxuXHJcbmludGVyZmFjZSBPRGF0YVNhdmVDb250ZXh0IGV4dGVuZHMgYnJlZXplLlNhdmVDb250ZXh0IHtcclxuICB0ZW1wS2V5czogYnJlZXplLkVudGl0eUtleVtdO1xyXG4gIGNvbnRlbnRLZXlzOiBicmVlemUuRW50aXR5W107XHJcbn1cclxuXHJcbi8qKiBAaGlkZGVuICovXHJcbmV4cG9ydCBjbGFzcyBEYXRhU2VydmljZU9EYXRhQWRhcHRlciBleHRlbmRzIGJyZWV6ZS5BYnN0cmFjdERhdGFTZXJ2aWNlQWRhcHRlciB7XHJcbiAgcmVsYXRpdmVVcmw6IGJvb2xlYW4gfCAoKGRzOiBicmVlemUuRGF0YVNlcnZpY2UsIHVybDogc3RyaW5nKSA9PiBzdHJpbmcpO1xyXG4gIC8vIF9jYXRjaE5vQ29ubmVjdGlvbkVycm9yID0gYWJzdHJhY3REc2FQcm90by5fY2F0Y2hOb0Nvbm5lY3Rpb25FcnJvcjtcclxuICAvLyBjaGFuZ2VSZXF1ZXN0SW50ZXJjZXB0b3IgPSBhYnN0cmFjdERzYVByb3RvLmNoYW5nZVJlcXVlc3RJbnRlcmNlcHRvcjtcclxuICAvLyBfY3JlYXRlQ2hhbmdlUmVxdWVzdEludGVyY2VwdG9yID0gYWJzdHJhY3REc2FQcm90by5fY3JlYXRlQ2hhbmdlUmVxdWVzdEludGVyY2VwdG9yO1xyXG4gIGhlYWRlcnMgPSB7IFwiRGF0YVNlcnZpY2VWZXJzaW9uXCI6IFwiMi4wXCIgfTtcclxuXHJcbiAgY29uc3RydWN0b3IoKSB7XHJcbiAgICBzdXBlcigpO1xyXG4gICAgdGhpcy5uYW1lID0gXCJPRGF0YVwiO1xyXG4gIH1cclxuXHJcbiAgc3RhdGljIHJlZ2lzdGVyKGNvbmZpZz86IGJyZWV6ZS5CcmVlemVDb25maWcpIHtcclxuICAgIGNvbmZpZyA9IGNvbmZpZyB8fCBicmVlemUuY29uZmlnO1xyXG4gICAgY29uZmlnLnJlZ2lzdGVyQWRhcHRlcihcImRhdGFTZXJ2aWNlXCIsIERhdGFTZXJ2aWNlT0RhdGFBZGFwdGVyKTtcclxuICAgIHJldHVybiBjb25maWcuaW5pdGlhbGl6ZUFkYXB0ZXJJbnN0YW5jZShcImRhdGFTZXJ2aWNlXCIsIFwiT0RhdGFcIiwgdHJ1ZSkgYXMgRGF0YVNlcnZpY2VPRGF0YUFkYXB0ZXI7XHJcbiAgfVxyXG5cclxuICBpbml0aWFsaXplKCkge1xyXG4gICAgT0RhdGEgPSBjb3JlLnJlcXVpcmVMaWIoXCJPRGF0YVwiLCBcIk5lZWRlZCB0byBzdXBwb3J0IHJlbW90ZSBPRGF0YSBzZXJ2aWNlc1wiKTtcclxuICAgIE9EYXRhLmpzb25IYW5kbGVyLnJlY29nbml6ZURhdGVzID0gdHJ1ZTtcclxuICB9XHJcblxyXG5cclxuICAvLyBBYnNvbHV0ZSBVUkwgaXMgdGhlIGRlZmF1bHQgYXMgb2YgQnJlZXplIDEuNS41LiAgXHJcbiAgLy8gVG8gdXNlIHJlbGF0aXZlIFVSTCAobGlrZSBwcmUtMS41LjUpLCBhZGQgYWRhcHRlckluc3RhbmNlLnJlbGF0aXZlVXJsID0gdHJ1ZTpcclxuICAvL1xyXG4gIC8vICAgICBsZXQgZHMgPSBicmVlemUuY29uZmlnLmluaXRpYWxpemVBZGFwdGVySW5zdGFuY2UoXCJkYXRhU2VydmljZVwiLCBcIndlYkFwaU9EYXRhXCIpO1xyXG4gIC8vICAgICBkcy5yZWxhdGl2ZVVybCA9IHRydWU7IFxyXG4gIC8vXHJcbiAgLy8gVG8gdXNlIGN1c3RvbSB1cmwgY29uc3RydWN0aW9uLCBhZGQgYWRhcHRlckluc3RhbmNlLnJlbGF0aXZlVXJsID0gbXlmdW5jdGlvbihkYXRhU2VydmljZSwgdXJsKTpcclxuICAvL1xyXG4gIC8vICAgICBsZXQgZHMgPSBicmVlemUuY29uZmlnLmluaXRpYWxpemVBZGFwdGVySW5zdGFuY2UoXCJkYXRhU2VydmljZVwiLCBcIndlYkFwaU9EYXRhXCIpO1xyXG4gIC8vICAgICBkcy5yZWxhdGl2ZVVybCA9IGZ1bmN0aW9uKGRhdGFTZXJ2aWNlLCB1cmwpIHtcclxuICAvLyAgICAgICAgcmV0dXJuIHNvbWVob3dDb252ZXJ0KHVybCk7XHJcbiAgLy8gICAgIH1cclxuICAvL1xyXG5cclxuICBmZXRjaE1ldGFkYXRhKG1ldGFkYXRhU3RvcmU6IGJyZWV6ZS5NZXRhZGF0YVN0b3JlLCBkYXRhU2VydmljZTogYnJlZXplLkRhdGFTZXJ2aWNlKSB7XHJcbiAgICBsZXQgc2VydmljZU5hbWUgPSBkYXRhU2VydmljZS5zZXJ2aWNlTmFtZTtcclxuXHJcbiAgICBsZXQgdXJsOiBzdHJpbmc7XHJcbiAgICBpZiAodGhpcy5yZWxhdGl2ZVVybCA9PT0gdHJ1ZSkge1xyXG4gICAgICB1cmwgPSBkYXRhU2VydmljZS5xdWFsaWZ5VXJsKCckbWV0YWRhdGEnKTtcclxuICAgIH0gZWxzZSBpZiAoY29yZS5pc0Z1bmN0aW9uKHRoaXMucmVsYXRpdmVVcmwpKSB7XHJcbiAgICAgIHVybCA9ICh0aGlzLnJlbGF0aXZlVXJsIGFzIGFueSkoZGF0YVNlcnZpY2UsICckbWV0YWRhdGEnKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHVybCA9IHRoaXMuZ2V0QWJzb2x1dGVVcmwoZGF0YVNlcnZpY2UsICckbWV0YWRhdGEnKTtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgbWhlYWRlcnMgPSBjb3JlLmV4dGVuZCh7fSwgdGhpcy5oZWFkZXJzKTtcclxuICAgIChtaGVhZGVycyBhcyBhbnkpLkFjY2VwdCA9ICdhcHBsaWNhdGlvbi8qOyBvZGF0YS5tZXRhZGF0YT1mdWxsJztcclxuXHJcbiAgICBsZXQgcHJvbWlzZSA9IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgLy8gT0RhdGEucmVhZCh1cmwsXHJcbiAgICAgIE9EYXRhLnJlYWQoe1xyXG4gICAgICAgIHJlcXVlc3RVcmk6IHVybCxcclxuICAgICAgICAvLyBoZWFkZXJzOiB7IFwiQWNjZXB0XCI6IFwiYXBwbGljYXRpb24vanNvblwifVxyXG4gICAgICAgIGhlYWRlcnM6IG1oZWFkZXJzXHJcbiAgICAgIH0sXHJcbiAgICAgICAgZnVuY3Rpb24gKGRhdGE6IGFueSkge1xyXG4gICAgICAgICAgLy8gZGF0YS5kYXRhU2VydmljZXMuc2NoZW1hIGlzIGFuIGFycmF5IG9mIHNjaGVtYXMuIHdpdGggcHJvcGVydGllcyBvZlxyXG4gICAgICAgICAgLy8gZW50aXR5Q29udGFpbmVyW10sIGFzc29jaWF0aW9uW10sIGVudGl0eVR5cGVbXSwgYW5kIG5hbWVzcGFjZS5cclxuICAgICAgICAgIGlmICghZGF0YSB8fCAhZGF0YS5kYXRhU2VydmljZXMpIHtcclxuICAgICAgICAgICAgbGV0IGVycm9yID0gbmV3IEVycm9yKFwiTWV0YWRhdGEgcXVlcnkgZmFpbGVkIGZvcjogXCIgKyB1cmwpO1xyXG4gICAgICAgICAgICByZXR1cm4gcmVqZWN0KGVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIGxldCBjc2RsTWV0YWRhdGEgPSBkYXRhLmRhdGFTZXJ2aWNlcztcclxuXHJcbiAgICAgICAgICAvLyBtaWdodCBoYXZlIGJlZW4gZmV0Y2hlZCBieSBhbm90aGVyIHF1ZXJ5XHJcbiAgICAgICAgICBpZiAoIW1ldGFkYXRhU3RvcmUuaGFzTWV0YWRhdGFGb3Ioc2VydmljZU5hbWUpKSB7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgbWV0YWRhdGFTdG9yZS5pbXBvcnRNZXRhZGF0YShjc2RsTWV0YWRhdGEpO1xyXG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgICAgICAgICAgcmV0dXJuIHJlamVjdChuZXcgRXJyb3IoXCJNZXRhZGF0YSBxdWVyeSBmYWlsZWQgZm9yIFwiICsgdXJsICsgXCI7IFVuYWJsZSB0byBwcm9jZXNzIHJldHVybmVkIG1ldGFkYXRhOiBcIiArIGUubWVzc2FnZSkpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBtZXRhZGF0YVN0b3JlLmFkZERhdGFTZXJ2aWNlKGRhdGFTZXJ2aWNlKTtcclxuICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICByZXR1cm4gcmVzb2x2ZShjc2RsTWV0YWRhdGEpO1xyXG5cclxuICAgICAgICB9LCBmdW5jdGlvbiAoZXJyb3I6IGFueSkge1xyXG4gICAgICAgICAgbGV0IGVyciA9IGNyZWF0ZUVycm9yKGVycm9yLCB1cmwpO1xyXG4gICAgICAgICAgZXJyLm1lc3NhZ2UgPSBcIk1ldGFkYXRhIHF1ZXJ5IGZhaWxlZCBmb3I6IFwiICsgdXJsICsgXCI7IFwiICsgKGVyci5tZXNzYWdlIHx8IFwiXCIpO1xyXG4gICAgICAgICAgcmV0dXJuIHJlamVjdChlcnIpO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgT0RhdGEubWV0YWRhdGFIYW5kbGVyXHJcbiAgICAgICk7XHJcbiAgICB9KTtcclxuXHJcbiAgICByZXR1cm4gcHJvbWlzZTtcclxuICB9XHJcblxyXG5cclxuXHJcbiAgZXhlY3V0ZVF1ZXJ5KG1hcHBpbmdDb250ZXh0OiBicmVlemUuTWFwcGluZ0NvbnRleHQpIHtcclxuICAgIGxldCB1cmw6IHN0cmluZztcclxuICAgIGlmICh0aGlzLnJlbGF0aXZlVXJsID09PSB0cnVlKSB7XHJcbiAgICAgIHVybCA9IG1hcHBpbmdDb250ZXh0LmdldFVybCgpO1xyXG4gICAgfSBlbHNlIGlmIChjb3JlLmlzRnVuY3Rpb24odGhpcy5yZWxhdGl2ZVVybCkpIHtcclxuICAgICAgdXJsID0gKHRoaXMucmVsYXRpdmVVcmwgYXMgYW55KShtYXBwaW5nQ29udGV4dC5kYXRhU2VydmljZSwgbWFwcGluZ0NvbnRleHQuZ2V0VXJsKCkpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgdXJsID0gdGhpcy5nZXRBYnNvbHV0ZVVybChtYXBwaW5nQ29udGV4dC5kYXRhU2VydmljZSwgbWFwcGluZ0NvbnRleHQuZ2V0VXJsKCkpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEFkZCBxdWVyeSBwYXJhbXMgaWYgLndpdGhQYXJhbWV0ZXJzIHdhcyB1c2VkXHJcbiAgICBsZXQgcXVlcnkgPSBtYXBwaW5nQ29udGV4dC5xdWVyeSBhcyBicmVlemUuRW50aXR5UXVlcnk7XHJcbiAgICBpZiAoIWNvcmUuaXNFbXB0eShxdWVyeS5wYXJhbWV0ZXJzKSkge1xyXG4gICAgICBsZXQgcGFyYW1TdHJpbmcgPSB0b1F1ZXJ5U3RyaW5nKHF1ZXJ5LnBhcmFtZXRlcnMpO1xyXG4gICAgICB1cmwgPSBhcHBlbmRRdWVyeVN0cmluZ1BhcmFtZXRlcih1cmwsIHBhcmFtU3RyaW5nKTtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgcHJvbWlzZSA9IG5ldyBQcm9taXNlPGJyZWV6ZS5RdWVyeVJlc3VsdD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICBPRGF0YS5yZWFkKHtcclxuICAgICAgICByZXF1ZXN0VXJpOiB1cmwsXHJcbiAgICAgICAgaGVhZGVyczogY29yZS5leHRlbmQoe30sIHRoaXMuaGVhZGVycylcclxuICAgICAgfSxcclxuICAgICAgICBmdW5jdGlvbiAoZGF0YTogYW55LCByZXNwb25zZTogYW55KSB7XHJcbiAgICAgICAgICBsZXQgaW5saW5lQ291bnQ6IGFueTtcclxuICAgICAgICAgIGlmIChkYXRhLl9fY291bnQpIHtcclxuICAgICAgICAgICAgLy8gT0RhdGEgY2FuIHJldHVybiBkYXRhLl9fY291bnQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgICAgaW5saW5lQ291bnQgPSBwYXJzZUludChkYXRhLl9fY291bnQsIDEwKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIC8vIE9kYXRhIHJldHVybnMgZGlmZmVyZW50IHJlc3VsdCBzdHJ1Y3R1cmUgd2hlbiBpdCByZXR1cm5zIG11bHRpcGxlIGVudGl0aWVzIChkYXRhLnJlc3VsdHMpIHZzIHNpbmdsZSBlbnRpdHkgKGRhdGEgZGlyZWN0bHkpLlxyXG4gICAgICAgICAgLy8gQHNlZSBodHRwOi8vd3d3Lm9kYXRhLm9yZy9kb2N1bWVudGF0aW9uL29kYXRhLXZlcnNpb24tMi0wL2pzb24tZm9ybWF0LyNSZXByZXNlbnRpbmdDb2xsZWN0aW9uc09mRW50cmllc1xyXG4gICAgICAgICAgLy8gYW5kIGh0dHA6Ly93d3cub2RhdGEub3JnL2RvY3VtZW50YXRpb24vb2RhdGEtdmVyc2lvbi0yLTAvanNvbi1mb3JtYXQvI1JlcHJlc2VudGluZ0VudHJpZXNcclxuICAgICAgICAgIGxldCByZXN1bHRzOiBhbnk7XHJcbiAgICAgICAgICBpZiAoZGF0YS5yZXN1bHRzKSB7XHJcbiAgICAgICAgICAgIHJlc3VsdHMgPSBkYXRhLnJlc3VsdHM7XHJcbiAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICByZXN1bHRzID0gZGF0YTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJldHVybiByZXNvbHZlKHsgcmVzdWx0czogcmVzdWx0cywgaW5saW5lQ291bnQ6IGlubGluZUNvdW50LCBodHRwUmVzcG9uc2U6IHJlc3BvbnNlLCBxdWVyeTogcXVlcnkgfSk7XHJcbiAgICAgICAgfSxcclxuICAgICAgICBmdW5jdGlvbiAoZXJyb3I6IGFueSkge1xyXG4gICAgICAgICAgcmV0dXJuIHJlamVjdChjcmVhdGVFcnJvcihlcnJvciwgdXJsKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICApO1xyXG4gICAgfSk7XHJcbiAgICByZXR1cm4gcHJvbWlzZTtcclxuICB9XHJcblxyXG5cclxuICBzYXZlQ2hhbmdlcyhvZGF0YVNhdmVDb250ZXh0OiBicmVlemUuU2F2ZUNvbnRleHQsIHNhdmVCdW5kbGU6IGJyZWV6ZS5TYXZlQnVuZGxlKTogUHJvbWlzZTxicmVlemUuU2F2ZVJlc3VsdD4ge1xyXG4gICAgbGV0IGFkYXB0ZXIgPSBvZGF0YVNhdmVDb250ZXh0LmFkYXB0ZXIgPSB0aGlzO1xyXG4gICAgbGV0IHNhdmVDb250ZXh0ID0gb2RhdGFTYXZlQ29udGV4dCBhcyBPRGF0YVNhdmVDb250ZXh0O1xyXG4gICAgbGV0IHVybDogc3RyaW5nO1xyXG4gICAgaWYgKHRoaXMucmVsYXRpdmVVcmwgPT09IHRydWUpIHtcclxuICAgICAgc2F2ZUNvbnRleHQucm91dGVQcmVmaXggPSBhZGFwdGVyLmdldFJvdXRlUHJlZml4KHNhdmVDb250ZXh0LmRhdGFTZXJ2aWNlKTtcclxuICAgICAgdXJsID0gc2F2ZUNvbnRleHQuZGF0YVNlcnZpY2UucXVhbGlmeVVybChcIiRiYXRjaFwiKTtcclxuICAgIH0gZWxzZSBpZiAoY29yZS5pc0Z1bmN0aW9uKGFkYXB0ZXIucmVsYXRpdmVVcmwpKSB7XHJcbiAgICAgIHNhdmVDb250ZXh0LnJvdXRlUHJlZml4ID0gKGFkYXB0ZXIucmVsYXRpdmVVcmwgYXMgRnVuY3Rpb