UNPKG

@aappddeevv/dynamics-client-ui

Version:

## What is it? A library to help you create great dynamics applications.

750 lines 36.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); /** * * Hacked CRMWebAPI from: https://github.com/davidyack/Xrm.Tools.CRMWebAPI */ const BuildSettings_1 = require("BuildSettings"); const XMLHTTPRequest_1 = require("./XMLHTTPRequest"); /** * Defaults to XMLHTTPRequest. * * @todo Add the ability to issue pure http query so we can fetch full links provided * in some odata responses. */ class CRMWebAPI { constructor(config) { this.GetList = (uri, QueryOptions) => tslib_1.__awaiter(this, void 0, void 0, function* () { var self = this; return new Promise(function (resolve, reject) { var url = self._BuildQueryURL(uri, QueryOptions ? QueryOptions : null, self.config); self.fetch(self.config, "GET", url, { 'headers': self._BuildQueryHeaders(QueryOptions ? QueryOptions : null, self.config) }, function (err, res) { if (err != false) { self._log('Errors', 'GetList Error:', res); reject(res); } else { var data = JSON.parse(res.response, CRMWebAPI.prototype._DateReviver); var nextLink = data['@odata.nextLink']; var recordCount = data['@odata.count']; var response = { List: data.value, Count: recordCount }; if (QueryOptions && QueryOptions.RecordAction) { response.List.forEach(record => QueryOptions.RecordAction(record)); response.List = []; } if (QueryOptions && QueryOptions.PageAction) { QueryOptions.PageAction(response.List); response.List = []; } // was 'undefined' a string !?!? if (nextLink === undefined) { resolve(response); } else { self.whilst(function () { return (nextLink !== undefined); }, function (callback) { self.fetch(self.config, "GET", nextLink, { 'headers': self._BuildQueryHeaders(QueryOptions ? QueryOptions : null, self.config) }, function (err, res) { if (err == false) { data = JSON.parse(res.response, CRMWebAPI.prototype._DateReviver); nextLink = data['@odata.nextLink']; response.List = response.List.concat(data.value); if (QueryOptions && QueryOptions.RecordAction) { response.List.forEach(function (record) { QueryOptions.RecordAction(record); }); response.List = []; } if (QueryOptions && QueryOptions.PageAction) { QueryOptions.PageAction(response.List); response.List = []; } callback(null, response.List.length); } else { self._log('Errors', 'GetList Error2', res); callback('err', 0); } }); }, function (err, n) { resolve(response); }); } } }); }); }); /** Issue a http fetch directly. The response is JSON parsed but not restructured. */ this.Fetch = (url, QueryOptions, method) => tslib_1.__awaiter(this, void 0, void 0, function* () { const self = this; return new Promise((resolve, reject) => { self.fetch(self.config, method ? method : "GET", url, { 'headers': self._BuildQueryHeaders(QueryOptions ? QueryOptions : null, self.config) }, (err, res) => { if (err != false) { self._log('Errors', 'Get Error', res); reject(res); } else { var data = JSON.parse(res.response, CRMWebAPI.prototype._DateReviver); resolve(data); } }); }); }); /// <summary> /// Get a collection or an instance of given entity type /// </summary> /// <param name="entityCollection" type="type">Entity logical name to retrieve including plural suffix</param> /// <param name="entityID" type="type">ID of requested record, or null for collection based on QueryOptions.Filter</param> /// <param name="QueryOptions" type="type"></param> this.Get = (entityCollection, entityID, QueryOptions) => tslib_1.__awaiter(this, void 0, void 0, function* () { const self = this; return new Promise(function (resolve, reject) { const url = entityID == null ? self._BuildQueryURL(entityCollection, QueryOptions ? QueryOptions : null, self.config) : self._BuildQueryURL(entityCollection + "(" + entityID.toString().replace(/[{}]/g, "") + ")", QueryOptions ? QueryOptions : null, self.config); self.fetch(self.config, "GET", url, { 'headers': self._BuildQueryHeaders(QueryOptions ? QueryOptions : null, self.config) }, function (err, res) { if (err != false) { self._log('Errors', 'Get Error', res); reject(res); } else { var data = JSON.parse(res.response, CRMWebAPI.prototype._DateReviver); resolve(data); } }); }); }); this.GetCount = (uri, QueryOptions) => tslib_1.__awaiter(this, void 0, void 0, function* () { var self = this; return new Promise(function (resolve, reject) { var url = self._BuildQueryURL(uri + "/$count", QueryOptions ? QueryOptions : null, self.config); self.fetch(self.config, "GET", url, { 'headers': self._BuildQueryHeaders(QueryOptions ? QueryOptions : null, self.config) }, function (err, res) { if (err != false) { self._log('Errors', 'GetCount Error', res); reject(res); } else { var data = parseInt(res.response); resolve(data); } }); }); }); /// <summary> /// Create a record /// </summary> /// <param name="entityCollection" type="type">Plural name of entity to create</param> /// <param name="data" type="type">JSON object with attributes for the record to create</param> this.Create = (entityCollection, data) => tslib_1.__awaiter(this, void 0, void 0, function* () { var self = this; return new Promise(function (resolve, reject) { var url = self.config.APIUrl + entityCollection; self._log('ODataUrl', url); self.fetch(self.config, "POST", url, { 'data': JSON.stringify(data) }, function (err, res) { if (err != false) { self._log('Errors', 'Create Error', res); reject(res); } else { const r = /\(([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\)/g; resolve(r.exec(res.headers["odata-entityid"])[1]); } }); }); }); /// <summary> /// Update an existing record or create a new if record does not exist (Upsert) /// </summary> /// <param name="entityCollection" type="type">Plural name of entity to update</param> /// <param name="key" type="type">Key to locate existing record</param> /// <param name="data" type="type">JSON object with attributes for the record to upddate</param> /// <param name="Upsert" type="type">Set to true to enable upsert functionality, which creates a new record if key is not found</param> this.Update = (entityCollection, key, data, Upsert = false) => tslib_1.__awaiter(this, void 0, void 0, function* () { var self = this; return new Promise(function (resolve, reject) { var url = self.config.APIUrl + entityCollection + '(' + key.replace(/[{}]/g, "") + ')'; self._log('ODataUrl', url); var payload = { "data": JSON.stringify(data), "headers": {} }; if (Upsert == false) payload["headers"]["If-Match"] = "*"; self.fetch(self.config, "PATCH", url, payload, function (err, res) { if (err != false) { self._log('Errors', 'Update Error', res); reject(res); } else { var response = {}; var parseEntityID = /\(([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\)/g.exec(res.headers["odata-entityid"]); if (parseEntityID != null) response.EntityID = parseEntityID[1]; resolve(response); } }); }); }); /// <summary> /// Delete an existing record /// </summary> /// <param name="entityCollection" type="type">Plural name of entity to delete</param> /// <param name="entityID" type="type">ID of record to delete</param> this.Delete = (entityCollection, entityID) => tslib_1.__awaiter(this, void 0, void 0, function* () { var self = this; return new Promise(function (resolve, reject) { var url = self.config.APIUrl + entityCollection + '(' + entityID.replace(/[{}]/g, "") + ')'; self._log('ODataUrl', url); self.fetch(self.config, "DELETE", url, {}, function (err, res) { if (err != false) { self._log('Errors', 'Delete Error', res); reject(res); } else { resolve(true); } }); }); }); this.Associate = (fromEntityCollection, fromEntityID, navProperty, toEntityCollection, toEntityID) => tslib_1.__awaiter(this, void 0, void 0, function* () { var self = this; return new Promise(function (resolve, reject) { var url = self.config.APIUrl + fromEntityCollection + '(' + fromEntityID.replace(/[{}]/g, "") + ')/' + navProperty + '/$ref'; self._log('ODataUrl', url); var payload = { 'data': JSON.stringify({ '@odata.id': self.config.APIUrl + toEntityCollection + '(' + toEntityID.replace(/[{}]/g, "") + ')' }) }; self.fetch(self.config, 'POST', url, payload, function (err, res) { if (err != false) { self._log('Errors', 'Associate Error', res); reject(res); } else { resolve(true); } }); }); }); this.DeleteAssociation = (fromEntityCollection, fromEntityID, navProperty, toEntityCollection, toEntityID) => tslib_1.__awaiter(this, void 0, void 0, function* () { var self = this; return new Promise(function (resolve, reject) { var url = self.config.APIUrl + fromEntityCollection + '(' + fromEntityID.replace(/[{}]/g, "") + ')/' + navProperty + '/$ref'; if (toEntityCollection != null && toEntityID != null) url += '?$id=' + self.config.APIUrl + toEntityCollection + '(' + toEntityID.replace(/[{}]/g, "") + ')'; self._log('ODataUrl', url); self.fetch(self.config, 'DELETE', url, {}, function (err, res) { if (err != false) { self._log('Errors', 'DeleteAssociation Error', res); reject(res); } else { resolve(true); } }); }); }); this.ExecuteFunction = (functionName, parameters, entityCollection = null, entityID = null) => tslib_1.__awaiter(this, void 0, void 0, function* () { var self = this; return new Promise(function (resolve, reject) { var parmvars = []; var parmvalues = []; var parmcount = 1; if (parameters != null) { Object.keys(parameters).forEach(function (key) { var val = parameters[key]; parmvars.push(key + "=" + "@p" + parmcount.toString()); if (typeof val === 'string' || val instanceof String) parmvalues.push("@p" + parmcount.toString() + "='" + val + "'"); else parmvalues.push("@p" + parmcount.toString() + "=" + val); parmcount++; }); } var url = ""; if (parameters != null) { url = self.config.APIUrl + functionName + "(" + parmvars.join(",") + ")?" + parmvalues.join("&"); if (entityCollection != null) url = self.config.APIUrl + entityCollection + "(" + entityID.toString().replace(/[{}]/g, "") + ")" + functionName + "(" + parmvars.join(",") + ")?" + parmvalues.join("&"); } else { url = self.config.APIUrl + functionName + "()"; if (entityCollection != null) url = self.config.APIUrl + entityCollection + "(" + entityID.toString().replace(/[{}]/g, "") + ")" + functionName + "()"; } self._log('ODataUrl', url); self.fetch(self.config, "GET", url, {}, function (err, res) { if (err != false) { self._log('Errors', 'ExecuteFunction Error', res); reject(res); } else { var data = JSON.parse(res.response, CRMWebAPI.prototype._DateReviver); resolve(data); } }); }); }); this.ExecuteAction = (actionName, data, entityCollection = null, entityID = null) => tslib_1.__awaiter(this, void 0, void 0, function* () { var self = this; return new Promise(function (resolve, reject) { var url = self.config.APIUrl + actionName; if (entityCollection != null) url = self.config.APIUrl + entityCollection + "(" + entityID.toString().replace(/[{}]/g, "") + ")/" + actionName; self._log('ODataUrl', url); self.fetch(self.config, "POST", url, { "data": JSON.stringify(data) }, function (err, res) { if (err != false) { self._log('Errors', 'ExecuteAction Error', res); reject(res); } else { if (res.response == "") { resolve(undefined); //changed from null } else { var data = JSON.parse(res.response, CRMWebAPI.prototype._DateReviver); resolve(data); } } }); }); }); this.GetOptionSetByName = (optionSetName) => tslib_1.__awaiter(this, void 0, void 0, function* () { var self = this; return new Promise(function (resolve, reject) { self.GetList('GlobalOptionSetDefinitions', { Select: ['Name'] }). then((r) => { r.List.forEach(set => { if (set.Name == optionSetName) { self.Get('GlobalOptionSetDefinitions', set.MetadataId). then(res => resolve(res), err => console.log(err)); } }); }, (e) => { console.log(e); reject(e); }); }); }); this.GetOptionSetUserLabels = (optionSetName) => tslib_1.__awaiter(this, void 0, void 0, function* () { var self = this; return new Promise(function (resolve, reject) { self.GetOptionSetByName(optionSetName). then((result) => { const displayList = result.Options.map((option) => { return { Value: option.Value, Label: option.Label.UserLocalizedLabel.Label }; }); resolve(displayList); }, err => { console.log(err); reject(err); }); }); }); this.config = config; this.fp = config.fetch || XMLHTTPRequest_1.fetch; this.shouldLog = config.shouldLog; this.log = config.log; //this.mkPromise = config.mkPromise || defaultMkPromise } //protected mkPromise: <T>(cb: PromiseArg<T>) => Promise<T> /** For now, pass in extra headers in payload.headers and data in payload.data. */ fetch(config, method, url, payload, callback) { if (this.fp) { const req = { method, url, data: payload.data, headers: payload.headers }; return this.fp(config, req, callback); } } _log(category, message, data = null) { if (this.shouldLog && this.log && this.shouldLog(category)) { this.log(category, message, data); } } _restParam(func, startIndex = null) { startIndex = startIndex == null ? func.length - 1 : +startIndex; return function () { var length = Math.max(arguments.length - startIndex, 0); var rest = Array(length); for (var index = 0; index < length; index++) { rest[index] = arguments[index + startIndex]; } switch (startIndex) { case 0: return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); } }; } whilst(test, iterator, callback) { if (test()) { var next = this._restParam(function (err, args) { if (err) { callback(err); } else if (test.apply(this, args)) { iterator(next); } else { callback.apply(null, [null].concat(args)); } }); iterator(next); } else { callback(null); } } _BuildQueryURL(uri, queryOptions, config) { const path = (queryOptions && queryOptions.Path) ? queryOptions.Path.map(part => typeof part === 'string' ? `/${part}` : `/${part.Property}` + (part.Type ? `/${part.Type}` : "")) : []; let fullurl = config.APIUrl + uri + (path.length > 0 ? path.join("/") : ""); const qs = []; if (queryOptions != null) { if (queryOptions.Select != null) qs.push("$select=" + encodeURI(queryOptions.Select.join(","))); if (queryOptions.OrderBy != null) qs.push("$orderby=" + encodeURI(queryOptions.OrderBy.join(","))); if (queryOptions.Filter != null) qs.push("$filter=" + encodeURI(queryOptions.Filter)); if (queryOptions.Expand != null) { var expands = []; queryOptions.Expand.forEach(function (ex) { if ((ex.Select != null) || (ex.Filter != null) || (ex.OrderBy != null) || (ex.Top != null)) { var qsExpand = []; if (ex.Select != null) qsExpand.push("$select=" + ex.Select.join(",")); if (ex.OrderBy != null) qsExpand.push("$orderby=" + ex.OrderBy.join(",")); if (ex.Filter != null) qsExpand.push("$filter=" + ex.Filter); if (ex.Top > 0) qsExpand.push("$top=" + ex.Top); expands.push(ex.Property + "(" + qsExpand.join(";") + ")"); } else expands.push(ex.Property); }); qs.push("$expand=" + encodeURI(expands.join(","))); } if (queryOptions.IncludeCount) qs.push("$count=true"); if (queryOptions.Skip && queryOptions.Skip > 0) qs.push("skip=" + encodeURI(queryOptions.Skip ? queryOptions.Skip.toString() : "")); if (queryOptions.Top && queryOptions.Top > 0) qs.push("$top=" + encodeURI(queryOptions.Top ? queryOptions.Top.toString() : "")); if (queryOptions.SystemQuery != null) qs.push("savedQuery=" + encodeURI(queryOptions.SystemQuery)); if (queryOptions.UserQuery != null) qs.push("userQuery=" + encodeURI(queryOptions.UserQuery)); if (queryOptions.FetchXml != null) qs.push("fetchXml=" + encodeURI(queryOptions.FetchXml)); } if (qs.length > 0) fullurl += "?" + qs.join("&"); this._log('ODataUrl', fullurl); return fullurl; } _BuildQueryHeaders(queryOptions, config) { var headers = {}; if (queryOptions != null) { if (queryOptions.FormattedValues == true) headers['Prefer'] = 'odata.include-annotations="OData.Community.Display.V1.FormattedValue"'; } return headers; } ; parseResponseHeaders(headerStr) { var headers = {}; if (!headerStr) { return headers; } var headerPairs = headerStr.split('\u000d\u000a'); for (var i = 0; i < headerPairs.length; i++) { var headerPair = headerPairs[i]; // Can't use split() here because it does the wrong thing // if the header value has the string ": " in it. var index = headerPair.indexOf('\u003a\u0020'); if (index > 0) { var key = headerPair.substring(0, index); var val = headerPair.substring(index + 2); headers[key.toLowerCase()] = val; } } return headers; } _DateReviver(key, value) { var a; if (typeof value === 'string') { a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); if (a) { return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6])); } } return value; } GetEntityDisplayNameList(LCID) { var self = this; return new Promise(function (resolve, reject) { self.GetList('EntityDefinitions', { Filter: 'IsPrivate eq false', Select: ['MetadataId', 'EntitySetName', 'DisplayName', 'DisplayCollectionName', 'LogicalName', 'LogicalCollectionName', 'PrimaryIdAttribute'] }). then(function (r) { var list = new Array(); r.List.forEach(function (entity) { var edm = new Object(); edm.MetadataId = entity.MetadataId; edm.EntitySetName = entity.EntitySetName; edm.LogicalName = entity.LogicalName; edm.LogicalCollectionName = entity.LogicalCollectionName; edm.PrimaryIdAttribute = entity.PrimaryIdAttribute; if ((entity.DisplayName.LocalizedLabels != null) && (entity.DisplayName.LocalizedLabels.length > 0)) { edm.DisplayName = entity.DisplayName.LocalizedLabels[0].Label; if (LCID != null) entity.DisplayName.LocalizedLabels.forEach(function (label) { if (label.LanguageCode == LCID) edm.DisplayName = label.Label; }); } else edm.DisplayName = edm.LogicalName; if ((entity.DisplayCollectionName.LocalizedLabels != null) && (entity.DisplayCollectionName.LocalizedLabels.length > 0)) { edm.DisplayCollectionName = entity.DisplayCollectionName.LocalizedLabels[0].Label; if (LCID != null) entity.DisplayCollectionName.LocalizedLabels.forEach(function (label) { if (label.LanguageCode == LCID) edm.DisplayCollectionName = label.Label; }); } else edm.DisplayCollectionName = entity.LogicalCollectionName; edm.LogicalDisplayName = edm.DisplayName + '(' + edm.LogicalName + ')'; edm.LogicalDisplayCollectionName = edm.DisplayCollectionName + '(' + edm.LogicalCollectionName + ')'; list.push(edm); }); resolve(list); }, function (e) { console.log(e); reject(e); }); }); } GetAttributeDisplayNameList(entityID, LCID) { var self = this; return new Promise(function (resolve, reject) { self.GetList('EntityDefinitions(' + entityID.toString() + ')/Attributes', { Filter: '((IsValidForRead eq true) and (AttributeOf eq null))', Select: ['MetadataId', 'DisplayName', 'LogicalName', 'SchemaName', 'AttributeType', 'IsPrimaryId'] }).then(function (r) { var list = new Array(); r.List.forEach(function (attrib) { var edm = new Object(); edm.MetadataId = attrib.MetadataId; edm.LogicalName = attrib.LogicalName; edm.SchemaName = attrib.SchemaName; edm.IsPrimaryId = attrib.IsPrimaryId; edm.AttributeType = attrib.AttributeType; if (attrib.AttributeType === "Lookup" || attrib.AttributeType === "Customer" || attrib.AttributeType === "Owner") edm.ODataLogicalName = "_" + attrib.LogicalName + "_value"; else edm.ODataLogicalName = attrib.LogicalName; if ((attrib.DisplayName.LocalizedLabels != null) && (attrib.DisplayName.LocalizedLabels.length > 0)) { edm.DisplayName = attrib.DisplayName.LocalizedLabels[0].Label; if (LCID != null) attrib.DisplayName.LocalizedLabels.forEach(function (label) { if (label.LanguageCode == LCID) edm.DisplayName = label.Label; }); } else edm.DisplayName = edm.LogicalName; edm.LogicalDisplayName = edm.DisplayName + '(' + edm.LogicalName + ')'; list.push(edm); }); resolve(list); }, function (e) { console.log(e); reject(e); }); }); } // // monkey patch // batch(entity, fetchXml, QueryOptions) { const self = this; // build the body var body = '--batch_contactfetch\n'; body += 'Content-Type: application/http\n'; body += 'Content-Transfer-Encoding: binary\n'; body += '\n'; body += 'GET ' + self.config.APIUrl + `${entity}?fetchXml=${encodeURIComponent(fetchXml)} HTTP/1.1\n`; body += 'Content-Type: application/json\n'; body += 'OData-Version: 4.0\n'; body += 'OData-MaxVersion: 4.0\n'; //if(QueryOptions && QueryOptions.BatchHeaders) { // console.log("BATCHE EXTRA HEADERS", QueryOptions.BatchHeaders) // const keys = Object.keys(QueryOptions!.BatchHeaders!) // if(keys && keys.length > 0) // body += keys.map(k => `${k}: ${QueryOptions!.BatchHeaders![k]}`).join("\n") // odata annotation: Microsoft.Dynamics.CRM.fetchxmlpagingcookie body += 'Prefer: odata.include-annotations="OData.Community.Display.V1.FormattedValue,Microsoft.Dynamics.CRM.*"\n'; //} body += '\n'; body += '--batch_contactfetch--'; return new Promise(function (resolve, reject) { const url = self._BuildQueryURL("$batch", QueryOptions ? QueryOptions : null, self.config); self._log('ODataUrl', url); //self.fetch(self.config, "POST", url, { // won't work, additive headers self._hack(self.config, "POST", url, { 'data': body, headers: { "Content-Type": "multipart/mixed;boundary=batch_contactfetch" } }, function (err, res) { if (err != false) { self._log('Errors', 'batch error', res); reject(res); } else { //if(DEBUG) console.log("raw r", res) var data = JSON.parse(self._sliceBatchResponse(res.response), self._DateReviver); if (data.error) { // its really an error self._log('Errors', 'batch error', res); reject(data.error); return; } //if(DEBUG) console.log("batch.raw json", data) var nextLink = data['@odata.nextLink']; var recordCount = data['@odata.count']; var fetchXmlPagingCookie = data["@Microsoft.Dynamics.CRM.fetchxmlpagingcookie"]; var response = { List: data.value, Count: recordCount, fetchXmlPagingCookie: fetchXmlPagingCookie }; if (QueryOptions && QueryOptions.RecordAction) { response.List.forEach(record => QueryOptions.RecordAction(record)); response.List = []; } if (QueryOptions && QueryOptions.PageAction) { QueryOptions.PageAction(response.List); response.List = []; } if (!nextLink) { resolve(response); } else { if (BuildSettings_1.DEBUG) console.log("NEXT LINK BUT NEED TO IMPLEMENT WHILST!!!"); if (BuildSettings_1.DEBUG) console.log("response", data); resolve(response); // the lib has page processing code here...copy and paste it! } } }); }); } _hack(config, method, url, payload, callback) { var self = this; var req = new XMLHttpRequest(); //req.open(method, encodeURI(url), true); req.open(method, url, true); if (config.AccessToken) req.setRequestHeader("Authorization", "Bearer " + config.AccessToken()); req.setRequestHeader("Accept", "application/json"); req.setRequestHeader("OData-MaxVersion", "4.0"); req.setRequestHeader("OData-Version", "4.0"); if (config.callerId) req.setRequestHeader("MSCRMCallerID", config.callerId); if (config.CallerID) req.setRequestHeader("MSCRMCallerID", config.CallerID); if (['POST', 'PUT', 'PATCH'].indexOf(method) >= 0) { // GL: Browser should set this itself //req.setRequestHeader("Content-Length", payload.data.length); //req.setRequestHeader("Content-Type", "application/json"); } if (payload.headers !== 'undefined') { for (var name in payload.headers) { req.setRequestHeader(name, payload.headers[name]); } } req.onreadystatechange = function () { if (this.readyState == 4 /* complete */) { req.onreadystatechange = () => { }; if ((this.status >= 200) && (this.status < 300)) { callback(false, { 'response': this.response, 'headers': self.parseResponseHeaders(this.getAllResponseHeaders()) }); } else { callback(true, this); } } }; if (['POST', 'PUT', 'PATCH'].indexOf(method) >= 0) { req.send(payload.data); } else { req.send(); } } _sliceBatchResponse(response) { const start = response.indexOf("{"); const end = response.lastIndexOf("}") + 1; //console.log("stats", start, end, response[start], response[end-1]) //console.log("parsing", response.substring(start, end)) return response.substring(start, end); } } exports.CRMWebAPI = CRMWebAPI; exports.default = CRMWebAPI; //# sourceMappingURL=CRMWebAPI.js.map