UNPKG

crmfetchkit

Version:

Library for Dynamics CRM that allows the execution of fetchxml-queries via JavaScript

287 lines (211 loc) 7.93 kB
var BusinessEntity = require('./BusinessEntity'); module.exports = (function() { 'use strict'; /// IE9/10 and Chrome, Firefox, support "textContent" and IE8 using "text" function getNodeText(node) { return node.text !== undefined ? node.text : node.textContent; } // get a single child node that matches the specified name. function getChildNode(xmlNode, nodeName) { var childNode; for (var i = 0, max = xmlNode.childNodes.length; i < max; i++) { childNode = xmlNode.childNodes[i]; if (childNode.nodeName === nodeName) { return childNode; } } } // Get the attribute regardless of the namespace function getAttribute(xmlNode, attrName) { var attr = null; for (var i = 0; i < xmlNode.attributes.length; i++) { attr = xmlNode.attributes[i]; if (attr.name === attrName) { return attr.value; } } } // retrievs the text-value of the expression function getChildNodeText(xml, xpathExpression) { return getNodeText(getChildNode(xml, xpathExpression)); } // injects the paging-cookie & page-count function setPagingDetails(fetchxml, pageNumber, pagingCookie) { var serializer = new XMLSerializer(), parser = new DOMParser(), fetchDoc = parser.parseFromString(fetchxml, 'text/xml'), fetchElem = fetchDoc.getElementsByTagName('fetch')[0]; fetchElem.setAttribute('page', pageNumber); fetchElem.setAttribute('paging-cookie', pagingCookie); return serializer.serializeToString(fetchDoc); } // parses a date-string in ISO-format into a date-object function dateObjectFromUTCString(utcString) { var s = utcString.split(/\D/); return new Date(Date.UTC(+s[0], --s[1], +s[2], +s[3], +s[4], +s[5], 0)); } // extracts the error message generated by the Dynamics CRM server function getSoapError(soapXmlObject) { var bodyNode, faultNode, faultStringNode; try { bodyNode = soapXmlObject.firstChild.firstChild; faultNode = getChildNode(bodyNode, 's:Fault'); faultStringNode = getChildNode(faultNode, 'faultstring'); return getNodeText(faultStringNode); } catch (e) { return "An error occurred when parsing the error returned from CRM server: " + e.message; } } // extracts the entity-name, totalrecord count, etc. // form the entity-collection xml-node function getEntityCollectionDetails(entityCollectionNode) { var entityName, moreRecords, pagingCookie, totalRecordCount, entitiesNode, collectionChildNode; // Try to get all child nodes in one pass for (var m = 0; m < entityCollectionNode.childNodes.length; m++) { collectionChildNode = entityCollectionNode.childNodes[m]; switch (collectionChildNode.nodeName) { case "a:EntityName": entityName = getNodeText(collectionChildNode); break; case "a:MoreRecords": moreRecords = getNodeText(collectionChildNode) === "true"; break; case "a:PagingCookie": pagingCookie = getNodeText(collectionChildNode); break; case "a:TotalRecordCount": totalRecordCount = parseInt(getNodeText(collectionChildNode), 10); break; case "a:Entities": entitiesNode = collectionChildNode; break; } } return { entityName: entityName, moreRecords: moreRecords, pagingCookie: pagingCookie, totalRecordCount: totalRecordCount }; } // parses "Attribute" nodes of the SOAP-response function parseAttibutes(attributesNode) { var typedAttrSet = {}, attrNode, key, type, value; for (var i = 0, max = attributesNode.childNodes.length; i < max; i++) { attrNode = attributesNode.childNodes[i]; // Establish the key for the attribute key = getChildNodeText(attrNode, 'b:key'); value = getChildNode(attrNode, 'b:value'); type = getAttribute(value, 'i:type'); // populate the object typedAttrSet[key] = xmlNodeToAttributeObject(type, value); } return typedAttrSet; } // Parses a single xml-node -> transforms into BusinessEntity function parseSingleEntityNode(entityNode) { var entity = new BusinessEntity(), childSet, item, key, value; entity.Id = getChildNodeText(entityNode, 'a:Id'); entity.attributes = parseAttibutes(getChildNode(entityNode, 'a:Attributes')); entity.logicalName = getChildNodeText(entityNode, 'a:LogicalName'); // parse the formated values childSet = getChildNode(entityNode, 'a:FormattedValues').childNodes; for (var i = 0, max = childSet.length; i < max; i++) { item = childSet[i]; key = getChildNodeText(item, 'b:key'); value = getChildNodeText(item, 'b:value'); entity.attributes[key].formattedValue = value; } return entity; } // get a list of entities from an attr of type 'EntityCollection' // e.g. 'Party Lists' function getEntityCollection(entityCollectionNode) { var entitiesNode = getChildNode(entityCollectionNode, 'a:Entities').childNodes, collectionDetails = getEntityCollectionDetails(entityCollectionNode), entities = []; for (var i = 0, max = entitiesNode.length; i < max; i++) { entities.push(parseSingleEntityNode(entitiesNode[i])); } return { entityName: collectionDetails.entityName, moreRecords: collectionDetails.moreRecords, pagingCookie: collectionDetails.pagingCookie, totalRecordCount: collectionDetails.totalRecordCount, entities: entities }; } // Converst the xml definiton into an attribute object. // The joined attributes are evaluated via a recursive call of this function function xmlNodeToAttributeObject(type, xmlnode) { var attr = { 'type': type }; switch (type) { case "a:OptionSetValue": attr.value = parseInt(getNodeText(xmlnode), 10); break; case "a:EntityReference": attr.guid = getChildNodeText(xmlnode, 'a:Id'); attr.name = getChildNodeText(xmlnode, 'a:Name'); attr.logicalName = getChildNodeText(xmlnode, 'a:LogicalName'); break; case "a:EntityCollection": attr.value = getEntityCollection(xmlnode); break; case "a:Money": attr.value = parseFloat(getNodeText(xmlnode), 10); break; case "a:AliasedValue": var aliasValue = getChildNode(xmlnode, 'a:Value'), aliasType = getAttribute(aliasValue, 'i:type'); // recursive call attr = xmlNodeToAttributeObject(aliasType, aliasValue); break; case 'c:int': attr.value = parseInt(getNodeText(xmlnode), 10); break; case 'c:decimal': attr.value = parseFloat(getNodeText(xmlnode)); break; case 'c:dateTime': attr.value = dateObjectFromUTCString(getNodeText(xmlnode)); break; case 'c:boolean': attr.value = (getNodeText(xmlnode) !== 'true') ? false : true; break; default: attr.value = getNodeText(xmlnode); break; } return attr; } // converts the response to a result-object that contains the // entities, pagaingcookie... function getFetchResult(responseXmlObject) { // "s:Envelope/s:Body/ExecuteResponse/ExecuteResult" var executeResult = responseXmlObject.firstChild.firstChild.firstChild.firstChild, resultsNode = getChildNode(executeResult, 'a:Results'), entityCollectionNode = getChildNode(resultsNode.firstChild, 'b:value'); return getEntityCollection(entityCollectionNode); } function getRetrieveResult(responseXmlObject) { var executeResult = responseXmlObject.firstChild.firstChild.firstChild.firstChild, resultsNode = getChildNode(executeResult, 'a:Results'), singleEntityNode = getChildNode(resultsNode.firstChild, 'b:value'); return parseSingleEntityNode(singleEntityNode); } /// /// Public API /// return { setPagingDetails: setPagingDetails, getSoapError: getSoapError, getFetchResult: getFetchResult, getRetrieveResult: getRetrieveResult }; }());