UNPKG

more-xrm

Version:

Create more applications using the Microsoft Dynamics Xrm platform, enables querying the dynamics data model from any application.

199 lines (198 loc) 8.98 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { GetRootQuery } from "../Query/Query"; import GetQueryXml from "../Query/QueryXml"; import { DefaultMaxRecords, DynamicsHeaders, WebApiVersion } from "./Dynamics"; import { formatDynamicsResponse } from "./DynamicsRequest"; export function dynamicsBatch(headers) { return new Batch(headers); } export function dynamicsBatchRequest(...url) { const batch = new Batch(); batch.requestAllUrls(url); return batch.execute(); } export function dynamicsBatchQuery(...query) { const batch = new Batch(); batch.requestAll(query); return batch.execute(); } class Batch { constructor(headers) { this.headers = headers; this.Changes = []; this.RelatedChanges = []; } execute() { return __awaiter(this, void 0, void 0, function* () { const results = yield Batch.requestBatch(`/api/data/${WebApiVersion}/$batch`, this.Changes, this.headers); if (this.RelatedChanges.length > 0) { for (let change of this.RelatedChanges) { let changeIndex = this.Changes.indexOf(change.relatedChange); let relatedId = results[changeIndex]; change.entityData[`${change.relatedPropertyName}@odata.bind`] = `${change.relatedChange.entitySetName}(${Batch.trimId(relatedId)})`; } const related = yield Batch.requestBatch(`/api/data/${WebApiVersion}/$batch`, this.RelatedChanges, this.headers); return results.concat(related); } else { return results; } }); } requestAllUrls(urls) { this.Changes.push.apply(this.Changes, urls.map(entitySetQuery => ({ entitySetQuery }))); return this; } requestAll(queries) { this.Changes.push.apply(queries.map(query => { const dataQuery = GetRootQuery(query); this.request(query); return { entitySetName: dataQuery.EntityPath, entitySetQuery: `fetchXml=${escape(GetQueryXml(query))}` }; })); return this; } request(query, maxRowCount = DefaultMaxRecords) { const dataQuery = GetRootQuery(query); if (!dataQuery.EntityPath) { throw new Error('dynamicsBatch request requires a Query object with an EntityPath'); } this.Changes.push({ entitySetName: dataQuery.EntityPath, entitySetQuery: `fetchXml=${escape(GetQueryXml(query, maxRowCount))}` }); return this; } deleteEntity(entitySetName, id) { this.Changes.push({ entitySetName: entitySetName, entityId: id, entityData: 'DELETE' }); return this; } saveEntity(entitySetName, data, id) { this.Changes.push({ entitySetName: entitySetName, entityId: id, entityData: data }); return this; } createRelatedEntity(entitySetName, data, navigationPropertyName) { let lastChange = this.Changes[this.Changes.length - 1]; if (!lastChange || lastChange.entityData == 'DELETE') throw new Error('createRelatedEntity relies on the previous change which was not found in the batch.'); if (lastChange.entityId) { data[`${navigationPropertyName}@odata.bind`] = `${lastChange.entitySetName}(${lastChange.entityId})`; this.Changes.push({ entitySetName: entitySetName, entityData: data }); } else { this.RelatedChanges.push({ entitySetName: entitySetName, entityData: data, relatedChange: lastChange, relatedPropertyName: navigationPropertyName }); } } static requestBatch(url, requests, headers) { const batchId = Batch.createId(); return fetch(url, { method: 'POST', headers: Object.assign({ 'Content-Type': `multipart/mixed;boundary=batch_${batchId}` }, DynamicsHeaders, headers), body: Batch.formatBatchRequest(batchId, requests) }) .then(response => Batch.formatBatchResponse(response.text())); } static formatBatchRequest(batchId, changes) { let batchBody = []; let requestBody = []; let changeNumber = 1; let changesetId = Batch.createId(); batchBody.push(`--batch_${batchId}`); batchBody.push(`Content-Type: multipart/mixed;boundary=changeset_${changesetId}`); batchBody.push(''); for (let change of changes) { if (change.entitySetQuery) { requestBody.push(`--batch_${batchId}`); requestBody.push('Content-Type: application/http'); requestBody.push('Content-Transfer-Encoding:binary'); requestBody.push(''); if (change.entitySetName) { requestBody.push(`GET ${encodeURI(`/api/data/${WebApiVersion}/${change.entitySetName}?${change.entitySetQuery}`)} HTTP/1.1`); } else { requestBody.push(`GET ${encodeURI(change.entitySetQuery)} HTTP/1.1`); } requestBody.push('Accept: application/json'); requestBody.push('Prefer: odata.include-annotations="OData.Community.Display.V1.FormattedValue"'); requestBody.push(''); } else { batchBody.push(`--changeset_${changesetId}`); batchBody.push('Content-Type: application/http'); batchBody.push('Content-Transfer-Encoding:binary'); batchBody.push(`Content-ID: ${changeNumber++}`); batchBody.push(''); batchBody.push(`${change.entityId ? 'PATCH' : 'POST'} ${encodeURI(`/api/data/${WebApiVersion}/${change.entitySetName}(${Batch.trimId(change.entityId)})`)} HTTP/1.1`); batchBody.push('Content-Type: application/json;type=entry'); batchBody.push(''); batchBody.push(JSON.stringify(change.entityData)); } } batchBody.push(`--changeset_${changesetId}--`); batchBody.push(requestBody.join('\n')); batchBody.push(`--batch_${batchId}--`); return batchBody.join('\n'); } static formatBatchResponse(responseText) { return responseText.then(response => { if (response) { if (response.indexOf('"innererror"') > -1 || response.indexOf('HTTP/1.1 500 Internal Server Error') > -1 || response.indexOf('HTTP/1.1 400 Bad Request') > -1) { throw new Error('Batch Request Error: ' + response); } else { let data = []; let responses = response.split('--changesetresponse'); for (let response of responses) { let contentId = ((/Content-ID:\s?(.*)\b/g).exec(response) || []).slice(1)[0]; let entityId = ((/OData-EntityId:[^(]*\((.*)\)/g).exec(response) || []).slice(1)[0]; data[contentId - 1] = entityId; } let requests = response.split('--batchresponse'); for (let request of requests) { //TODO: determine better way of identifying request responses if (request.indexOf('OData.Community.Display.V1.FormattedValue') > -1) { let responseIndex = request.indexOf('{'); let json = request.substring(responseIndex); let item = JSON.parse(json); data.push(formatDynamicsResponse(item)); } } return data; } } }); } static createId() { return 'id' + Math.random().toString(16).slice(2); } static trimId(id) { return (id || '').replace(/{|}/g, ''); } }