/** * * Hacked CRMWebAPI from: https://github.com/davidyack/Xrm.Tools.CRMWebAPI */ import { DEBUG } from "BuildSettings" export type Id = string export type URI = string import { fetch } from "./XMLHTTPRequest" export interface Request { /** POST, PUT, etc. */ method: string /** Full url. */ url: URI /** Extra headers in addition to what fetch may do. */ headers?: Record /** Payload. */ data: any } export interface Config { APIUrl: URI AccessToken?: () => string callerId?: Id CallerID?: Id Log?: any } export type Fetcher = (config: Config, request: Request, callback: (error: boolean, ctx: any) => void) => void /** Provide a fetcher strategy. */ export interface FetchProvider { fetch?: Fetcher } /* type Resolve = (v?: T|Promise) => void type Reject = (e?: any) => void type PromiseArg = (thunks: (res: Resolve, rej: Reject) => void) => void export interface PromiseMaker { mkPromise?: (cb: PromiseArg) => Promise } export function defaultMkPromise(cb: PromiseArg) { return new Promise(cb) } */ /** Provide a logger. */ export interface Logger { shouldLog?: (category: string) => boolean log?: (category: string, message: string, ctx: any) => void } export interface GetListResponse { List: T[] Count?: number fetchXmlPagingCookie?: string } export interface UpdateResponse { EntityID: Id } export interface Attribute { Property: string Type?: string } export interface QueryOptionBase { FormattedValues?: boolean Select?: string[] Filter?: string /** List of strings "attributename desc/asc" */ OrderBy?: string[] Top?: number Path?: Array } export interface ExpandQueryOptions extends QueryOptionBase { Property: string } export interface QueryOptions extends QueryOptionBase { Expand?: ExpandQueryOptions[] FetchXml?: string IncludeCount?: boolean Skip?: number SystemQuery?: string UserQuery?: string RecordAction?: (record: any) => void PageAction?: (list: any[]) => void BatchHeaders?: Record /** Debug tag. */ tag?: string } export interface Option { Value: number Label: string } /** General purpose dynamics web api client. */ export interface _Client_IN_PROGRESS_ { GetList(uri: URI, QueryOptions?: QueryOptions): Promise> Get(entityCollection: string, entityID: Id | null, QueryOptions?: QueryOptions): Promise GetCount(uri: URI, QueryOptions?: QueryOptions) Create(entityCollection: string, data: any): Promise Update(entityCollection: string, key: string, data: any, Upsert: boolean): Promise Delete(entityCollection: string, entityID: Id): Promise Associate(fromEntityCollection: string, fromEntityID: Id, navProperty: String, toEntityCollection: string, toEntityID: Id): Promise DeleteAssociation(fromEntityCollection: string, fromEntityID: Id, navProperty: string, toEntityCollection: string, toEntityID: Id): Promise ExecuteFunction(functionName: string, parameters: any, entityCollection: string | null, entityID: Id | null): Promise ExecuteAction(actionName: string, data: any, entityCollection: string | null, entityID: Id | null): Promise batch(entity: string, fetchXml: string, QueryOptions?: QueryOptions): Promise> } /** * Defaults to XMLHTTPRequest. * * @todo Add the ability to issue pure http query so we can fetch full links provided * in some odata responses. */ export class CRMWebAPI { constructor(config: Config & FetchProvider & Logger) { this.config = config this.fp = config.fetch || fetch this.shouldLog = config.shouldLog this.log = config.log //this.mkPromise = config.mkPromise || defaultMkPromise } protected fp: Fetcher private config: Config protected shouldLog?: (category: string) => boolean protected log?: (category: string, message: string, ctx: any) => void //protected mkPromise: (cb: PromiseArg) => Promise /** For now, pass in extra headers in payload.headers and data in payload.data. */ protected fetch(config: Config, method: string, url: URI, payload: any, callback: (error: boolean, ctx: any) => void) { if (this.fp) { const req = { method, url, data: payload.data, headers: payload.headers } return this.fp(config, req, callback) } } private _log(category: string, message: string, data: any = null) { if (this.shouldLog && this.log && this.shouldLog(category)) { this.log(category, message, data) } } private _restParam(func: Function, startIndex: number | null = 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); } }; } private 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) } } public GetList = async (uri: URI, QueryOptions?: QueryOptions): Promise> => { 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: GetListResponse = { 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. */ public Fetch = async(url: string, QueryOptions?: QueryOptions, method?: string): Promise => { 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) } }) }) } /// /// Get a collection or an instance of given entity type /// /// Entity logical name to retrieve including plural suffix /// ID of requested record, or null for collection based on QueryOptions.Filter /// public Get = async (entityCollection: string, entityID: Id | null, QueryOptions?: QueryOptions): Promise => { 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) } }) }) } public GetCount = async (uri: URI, QueryOptions?: QueryOptions) => { 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) } }); }); } /// /// Create a record /// /// Plural name of entity to create /// JSON object with attributes for the record to create public Create = async (entityCollection: string, data: any): Promise => { 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]) } }); }); } /// /// Update an existing record or create a new if record does not exist (Upsert) /// /// Plural name of entity to update /// Key to locate existing record /// JSON object with attributes for the record to upddate /// Set to true to enable upsert functionality, which creates a new record if key is not found public Update = async (entityCollection: string, key: string, data: any, Upsert: boolean = false) => { 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: any = {} 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); } }); }); }; /// /// Delete an existing record /// /// Plural name of entity to delete /// ID of record to delete public Delete = async (entityCollection: string, entityID: Id): Promise => { 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) } }); }); } public Associate = async (fromEntityCollection: string, fromEntityID: Id, navProperty: String, toEntityCollection: string, toEntityID: Id): Promise => { 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) } }); }); } public DeleteAssociation = async (fromEntityCollection: string, fromEntityID: Id, navProperty: string, toEntityCollection: string, toEntityID: Id): Promise => { 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) } }); }); } public ExecuteFunction = async (functionName: string, parameters: any, entityCollection: string | null = null, entityID: Id | null = null) => { var self = this; return new Promise(function(resolve, reject) { var parmvars: string[] = [] var parmvalues: string[] = [] var parmcount: number = 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) } }); }); } public ExecuteAction = async (actionName: string, data: any, entityCollection: string | null = null, entityID: Id | null = null) => { 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) } } }) }) } private _BuildQueryURL(uri: URI, queryOptions: QueryOptions | null, config: 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: string[] = [] 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: string[] = []; queryOptions.Expand.forEach(function(ex: any) { if ((ex.Select != null) || (ex.Filter != null) || (ex.OrderBy != null) || (ex.Top != null)) { var qsExpand: string[] = []; 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; } private _BuildQueryHeaders(queryOptions: QueryOptions | null, config: Config) { var headers = {}; if (queryOptions != null) { if (queryOptions.FormattedValues == true) headers['Prefer'] = 'odata.include-annotations="OData.Community.Display.V1.FormattedValue"'; } return headers; }; private parseResponseHeaders(headerStr: string) { 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; } public _DateReviver(key: string, value: any) { 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; } public GetOptionSetByName = async (optionSetName: string): Promise => { var self = this; return new Promise(function(resolve, reject) { self.GetList('GlobalOptionSetDefinitions', { Select: ['Name'] }). then((r: GetListResponse) => { r.List.forEach(set => { if (set.Name == optionSetName) { self.Get('GlobalOptionSetDefinitions', set.MetadataId). then(res => resolve(res), err => console.log(err)) } }) }, (e: Error) => { console.log(e) reject(e) }) }) } public GetOptionSetUserLabels = async (optionSetName: string): Promise> => { var self = this; return new Promise>(function(resolve, reject) { self.GetOptionSetByName(optionSetName). then((result: { Options: Array }) => { const displayList = result.Options.map((option: any) => { return { Value: option.Value, Label: option.Label.UserLocalizedLabel.Label } }) resolve(displayList) }, err => { console.log(err) reject(err) } ) }) } public GetEntityDisplayNameList(LCID: string) { 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: any) { var list = new Array(); r.List.forEach(function(entity: any) { var edm: any = 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) }) }); } public GetAttributeDisplayNameList(entityID: string, LCID: string) { 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: any) { var list = new Array(); r.List.forEach(function(attrib) { var edm: any = 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 // public batch(entity: string, fetchXml: string, QueryOptions?: QueryOptions): Promise> { 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) { // callback 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: GetListResponse = { 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 (DEBUG) console.log("NEXT LINK BUT NEED TO IMPLEMENT WHILST!!!") if (DEBUG) console.log("response", data) resolve(response) // the lib has page processing code here...copy and paste it! } } }) }) } private _hack(config: Config, method: string, url: URI, payload: any, callback: (succes: boolean, ctx: any) => void) { 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() } } private _sliceBatchResponse(response: string) { 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) } } export default CRMWebAPI