"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var DataTable_1 = require("./DataTable"); var Guid_1 = require("./Guid"); var Fetch_1 = require("./Fetch"); var Messages_1 = require("./Messages"); var CRMDataTypes_1 = require("./CRMDataTypes"); var ImportExportUtil_1 = require("./ImportExportUtil"); var StateUtil_1 = require("./StateUtil"); var MetadataUtil_1 = require("./MetadataUtil"); var path = require("path"); var edge = require("edge"); var debug = require("debug")("dynamicsnode"); var debugQueries = require("debug")("dynamicsnode:queries"); var CRMClient = (function () { /** * Default constructor * @classdesc Allow access to CRM functions. Contains the functions to interact with CRM services. * @class CRMClient * @param {string} connectionString Optional. A valid connection string or connection string name. * The connection string can be either a valid connection string or a name of an existing connection string in the file "config.json" at the root path of your application. * If no value is passed to the constructor, the "default" text will be assumed, which means that a connection string named "default" will be used. * @see {@link https://msdn.microsoft.com/en-ie/library/mt608573.aspx} for further information * * @example <caption>config.json file format</caption> * { * "connectionStrings": * { * "default":"AuthType=Office365; Url=http://crm.contoso.com/xrmContoso; Domain=CONTOSO; Username=jsmith; Password=passcode", * "connection2":"AuthType=AD; Url=http://crm.contoso.com/xrmContoso" * } * } * @example <caption>Create a connection using a valid Connection String</caption> * var crm = new CRMClient("AuthType=Office365; Url=http://crm.contoso.com/xrmContoso; Domain=CONTOSO; Username=jsmith; Password=passcode"); * @example <caption>Create a connection using the connection string named "connection2" specified in the config.json file</caption> * var crm = new CRMClient("connection2"); * @example <caption>Create a connection using the connection string named "default" specified in the config.json file</caption> * var crm = new CRMClient(); * @example <caption>Create a connection using the connection string named "default" specified in the config.json file</caption> * var crm = new CRMClient("default"); */ function CRMClient(connectionString, fakeBridge) { if (connectionString === void 0) { connectionString = "default"; } if (fakeBridge === void 0) { fakeBridge = false; } this.connectionString = connectionString; var config = this.tryGetModule(path.join(process.cwd(), "config.json")); if (config && config.connectionStrings && config.connectionStrings[connectionString]) { this.connectionString = config.connectionStrings[connectionString]; } this._crmBridge = this.getBridge(fakeBridge); this.testConnection(); } /** * Gets the bridge object between node and .net * @private * @method CRMClient#getBridge * @param fakeBridge {boolean} indicates if a fake bridge whants to be retrieved * @returns a .net bridge that allows node to interact with .net */ CRMClient.prototype.getBridge = function (fakeBridge) { var assemblyFile = path.join(__dirname, "bin/DynamicsNode.dll"); var refs = [path.join(__dirname, "bin/Microsoft.Crm.Sdk.Proxy.dll"), path.join(__dirname, "bin/Microsoft.Xrm.Tooling.Connector.dll"), path.join(__dirname, "bin/Microsoft.Xrm.Sdk.dll"), "System.Runtime.Serialization.dll", "System.ServiceModel.dll", path.join(__dirname, "bin/Microsoft.IdentityModel.Clients.ActiveDirectory.dll") ]; var createBridge = edge.func({ assemblyFile: assemblyFile, references: refs }); var bridge = createBridge({ connectionString: this.connectionString, useFake: fakeBridge }, true); return bridge; }; CRMClient.prototype.tryGetModule = function (moduleId) { var result = null; try { result = require(moduleId); } catch (e) { } return result; }; CRMClient.prototype.convert = function (propertiesArray) { var converted = null; if (propertiesArray) { converted = {}; for (var i = 0; i < propertiesArray.length; i++) { var propValue = propertiesArray[i]; if (propValue[1] instanceof Array) { var convertedValues = []; for (var j = 0; i < propValue[1].length; i++) { var arrayItem = propValue[1][j]; convertedValues.push(this.convert(arrayItem)); } converted[propValue[0]] = convertedValues; } else { converted[propValue[0]] = propValue[1]; } } } return converted; }; /** * Returns information about the current user. Useful for testing the active connection. * @returns a {@link WhoAmIResponse} object with the information about the authenticated user in CRM. * @method CRMClient#whoAmI * @example <caption>Returns information about the current user</caption> * var who = crm.whoAmI(); * console.log(who.BusinessUnitId); // prints 6fefeb79-5447-e511-a5db-0050568a69e2 * console.log(who.OrganizationId); // prints 2b476bd1-aaed-43ee-b386-eee0f1b87207 * console.log(who.UserId); // prints 9ba35c25-b892-4f8a-b124-3920d9873af4 */ CRMClient.prototype.whoAmI = function () { var request = new Messages_1.WhoAmIRequest(); var response = this.Execute(request); return response; }; /** * Tests the active connection. Throws an exception if there's any error. * The method performs a {@link WhoAmIRequest}. * @method CRMClient#testConnection * @see CRMClient#whoAmI */ CRMClient.prototype.testConnection = function () { try { this.whoAmI(); // Performs a who am i request } catch (e) { var error = new Error(); throw new Error("Error trying to connect to server\n" + JSON.stringify(e)); } }; /** * Retrieves one single record from CRM. * @method CRMClient#retrieve * @param entityName {string} Name of the entity to be retrieved. The name is case insensitive, so all values are lowercased before being sent to CRM. * @param idOrConditions {string|Guid|object} Either a string with the GUID if the record to be retrieved, a {@link Guid} object with the same value, or a conditions object that returns only one record. * Learn how to write condition objects: {@link Fetch#setFilter} * @param attributes {string|string[]|boolean} Optional. Either an attribute name, an array of attributes, or a true value indicating that all attributes must be retrieved. The default value is true. An ***** value has the same effect * * @returns A javascript object containing the values of the record. If no data found, then a null object is returned. * * @example <caption>Return all the columns for the specified account id</caption> * var account = crm.retrieve("account","6fefeb79-5447-e511-a5db-0050568a69e2"); * console.log(account); * @example <caption>Return all the columns for the specified account id</caption> * var account = crm.retrieve("account","6fefeb79-5447-e511-a5db-0050568a69e2","*"); * console.log(account); * @example <caption>Return all the columns for the specified account id</caption> * var account = crm.retrieve("account","6fefeb79-5447-e511-a5db-0050568a69e2",true); * console.log(account); * @example <caption>You can use the Guid class to specify the id parameter. This allows to perform a GUID format validation before calling the method.</caption> * var Guid = require("dynamicsnode").Guid; * var account = crm.retrieve("account",new Guid("6fefeb79-5447-e511-a5db-0050568a69e2")); * console.log(account); * @example <caption>Get the accountid,name,ownerid,createdon columns for the given account id</caption> * var account = crm.retrieve("account","6fefeb79-5447-e511-a5db-0050568a69e2",["accountid","name","ownerid","createdon"]); * console.log(account); * @example <caption>Get the name of the specified account</caption> * var account = crm.retrieve("account","6fefeb79-5447-e511-a5db-0050568a69e2","name"); * console.log(account.name); * @example <caption>Accessing information about a related record</caption> * var account = crm.retrieve("account","6fefeb79-5447-e511-a5db-0050568a69e2","ownerid"); * console.log(account.ownerid); // outputs the GUID value * console.log(account.ownerid_type); // outputs systemuser * console.log(account.ownerid_name); // outputs John Doe * @example <caption>Returns an account using a condition object. If there are more than one account named "Acme" then an exception will be thrown</caption> * var account = crm.retrieve("account",{name:"Acme"}); * console.log(account.name); */ CRMClient.prototype.retrieve = function (entityName, idOrConditions, pColumns) { var idValue; var result; var columns; if (!entityName) throw new Error("Entity name not specified"); if (!idOrConditions) throw new Error("Id or Conditions not specified"); entityName = entityName.toLocaleLowerCase(); // normalize casing // validate columns if (pColumns === undefined) { columns = true; // default value } else { columns = pColumns; if (typeof pColumns == "string") { columns = [pColumns]; } if (Array.isArray(columns)) { for (var i = 0; i < columns.length; i++) { columns[i] = columns[i].toLocaleLowerCase(); // normalize casing; } } } if (idOrConditions instanceof Guid_1.Guid) { idValue = idOrConditions.getValue(); } else if (typeof idOrConditions === "string" || idOrConditions instanceof String) { idValue = idOrConditions; if (!Guid_1.Guid.isGuid(idValue)) throw new Error("Invalid GUID value"); } else if (typeof idOrConditions === "object") { // Assume a conditions objet was passed // Get the records that meet the specified criteria var idField = this.getIdField(entityName); var foundRecords = this.retrieveMultiple(entityName, idOrConditions, idField); if (foundRecords.rows !== null) { if (foundRecords.rows.length > 1) throw new Error("Too many records found matching the specified criteria"); if (foundRecords.rows.length > 0) { // TODO: Refactor to avoid querying CRM twice idValue = foundRecords.rows[0][idField]; } } } else { throw new Error("invalid idOrConditions type value"); } if (idValue) { var params = { entityName: entityName, id: idValue, columns: columns }; var retrieveResult; try { retrieveResult = this._crmBridge.Retrieve(params, true); } catch (ex) { var rethrow = false; if (ex.Detail && ex.Detail.InnerFault && ex.Detail.InnerFault.Message) { // Record with specified Id doesn't exists var msg = entityName + " With Id = " + idValue.toLowerCase().replace("{", "").replace("}", "") + " Does Not Exist"; if (ex.Detail.InnerFault.Message != msg) rethrow = true; } if (rethrow) throw ex; } // convert the result to a js object if (retrieveResult != null) { result = this.convert(retrieveResult); } } return result; }; /** * Retrieves one or more records of the specified entity that fulfill the specified conditions. * @method CRMClient#retrieveMultiple * * @param entityNameOrFetchXml {string} Name of the entity or Fetch Xml query to specify the records to be retrieved. More info: {@link https://msdn.microsoft.com/en-us/library/gg328332.aspx} * @param conditions {object} Optional. In case you don't want to write a FetchXml to specify the records to retrieve, you can use a conditions object to specify the criteria to retrieve records. If you omit this parameter, all the existing records of the specified entity will be retrieved; omitting this parameter is equivalent to write a FetchXml without any filter conditions. * Learn how to write condition objects: {@link Fetch#setFilter} * @param attributes {string|string[]|boolean} Optional. Either an attribute name, an array of attributes, or a true value indicating that all attributes must be retrieved. The default value is true. An ***** value has the same effect * @returns {DataTable} {@link DataTable} object with the records found. * * @see Build queries with FetchXML: {@link https://msdn.microsoft.com/en-us/library/gg328332.aspx} * * @example <caption>Retrieves all the records of the account entity. Only the accountid column will be retrieved (the id column is always returned in all Crm queries)</caption> * var accounts = crm.retrieveMultiple("<fetch><entity name='account'></entity></fetch>"); * @example <caption>Retrieves all the records of the account entity. Includes also all the columns of the entity.</caption> * var accounts = crm.retrieveMultiple("account"); * @example <caption>Retrieves all the records of the account entity where the account name is equals to "contoso". Returns all the columns of the entity.</caption> * var accounts = crm.retrieveMultiple("account",{name:"contoso"}); * @example <caption>Retrieves all the records of the account entity where the account name is equals to "contoso", but only the specified columns are included in the query.</caption> * var accounts = crm.retrieveMultiple("account",{name:"contoso"},["accountid","name","ownerid","createdon"]); * @example <caption>Retrieves all the records of the account entity where the account name is equals to "contoso" or "acme". Returns all the columns of the entity.</caption> * var accounts = crm.retrieveMultiple("account",{name:["contoso","acme"]}); * */ CRMClient.prototype.retrieveMultiple = function (entityNameOrFetchXml, conditions, attributes) { var result = new Array(); var fetchXml; if (!(conditions || attributes)) { // No conditions or attributes were specified, means a FetchXml value is expected. // TODO: Improve this. could use a regular expression to distinguish between an xml and an entity name? fetchXml = entityNameOrFetchXml; } else { var fetch = new Fetch_1.Fetch(entityNameOrFetchXml); if (conditions) { fetch.setFilter(conditions); } if (attributes) { fetch.setAttributes(attributes); } fetchXml = fetch.toString(); debugQueries(fetchXml); } var retrieveResult = this._crmBridge.RetrieveMultiple(fetchXml, true); for (var i = 0; i < retrieveResult.length; i++) { var record = retrieveResult[i]; var convertedRecod = this.convert(record); result.push(convertedRecod); } var dt = new DataTable_1.DataTable(entityNameOrFetchXml, result); return dt; }; /** It is a simpified way of retrieving all the existing records of an entity. Is equivalent to call the {@link CRMClient#retrieveMultiple} method not specifying the conditions or attributes method * @method CRMClient#retrieveAll * @param entityName {string} Name of the entity which records you want to retrieve. * @returns {DataTable} {@link DataTable} object with the records found. * @example <caption>Retrieve all existing account records</caption> * var accounts = crm.retrieveAll("account"); */ CRMClient.prototype.retrieveAll = function (entityName) { var fetch = new Fetch_1.Fetch(entityName, "*"); var fetchXml = fetch.toString(); var result = this.retrieveMultiple(fetchXml); return result; }; /** * Creates a record in CRM. The names in the entity or attributes are case insensitive, so all the names will be lowercased before * send the operation to Crm. * @method CRMClient#create * @param entityName {string} The name of the entity which record you want to create * @param attributes {object} Javascript object with the values the new record will have. * * @returns {string} GUID of the record created. * * @example <caption>Create an account named "Contoso"</caption> * var accountid = crm.create("account",{name:"contoso",description:"this is a test",AccountCategoryCode:1}); * console.log(accountid); * @example <caption>Create an email with activity parties</caption> * var contact1Id = "{6633f95b-c146-45d4-ae99-6bd84f9bf7bc}" * var contact2Id = "{6633f95b-c146-45d4-ae99-6bd84f9bf7bc}" * var userId = "{6633f95b-c146-45d4-ae99-6bd84f9bf7bc}" * var email = { * To : [{id:contact1Id,type:"contact"},{id:contact2Id,type:"contact"}], * From : [{id:userId,type:"systemuser"}], * Subject : "Test Email", * Description : "Test Email", * DirectionCode : true * }; * var emailId = crm.create("email",email); */ /** * Creates records in CRM using the rows defined in the DataTable. * The id attribute of every created record will be updated with the generated CRM GUID. * You need to set the name of the table with the entity name that you want to create. * @method CRMClient#create * @param data {DataTable} DataTable with the data to be created in CRM. * * @example <caption>Create a set of accounts using an excel file</caption> * var accountsToLoad = DataTable.load("AccountsToLoad.xlsx"); * crm.create(accountsToLoad); * console.log(accountsToLoad.rows[0].accountid); // This will output the GUID of the created record */ CRMClient.prototype.create = function (entityNameOrTable, attributes) { var retVal = null; if (entityNameOrTable instanceof DataTable_1.DataTable) { if (entityNameOrTable.name == null) throw new Error("Table name not specified"); debug("Preparing to create " + entityNameOrTable.rows.length + " records from a DataTable. Entity " + entityNameOrTable.name + "..."); var primaryAttribute = this.getIdField(entityNameOrTable.name); debug("Primary Attribute:" + primaryAttribute); for (var i = 0; i < entityNameOrTable.rows.length; i++) { debug(i + " of " + entityNameOrTable.rows.length + "..."); var row = entityNameOrTable.rows[i]; var recordId = this.createInternal(entityNameOrTable.name, row); // update the record id in the table row[primaryAttribute] = recordId; } } else { if (attributes === undefined) throw new Error("The attributes parameter is required"); debug("Preparing to create a " + entityNameOrTable + " record..."); retVal = this.createInternal(entityNameOrTable, attributes); } return retVal; }; CRMClient.prototype.createInternal = function (entityName, attributes) { var entity = this.ConvertToEntity(entityName, attributes); var createdGuid = this._crmBridge.Create(entity, true); return createdGuid; }; CRMClient.prototype.ConvertToEntity = function (entityName, attributes) { // perform some validations if (!entityName) throw new Error("Entity name not specified"); if (!attributes) throw new Error("Attributes not specified"); entityName = entityName.toLocaleLowerCase(); // normalize casing var entityMetadata = this.getEntityMetadata(entityName); debug("Converting attributes to CRM Entity type..."); var entity = new CRMDataTypes_1.Entity(); entity.LogicalName = entityName; entity.Attributes = {}; for (var prop in attributes) { var attributeValue = null; // get the attribute from metadata var attributeMetadata = MetadataUtil_1.MetadataUtil.getAttributeMetadata(this, entityName, prop.toLocaleLowerCase()); if (attributeMetadata) { if (attributes[prop] !== null) { if (attributeMetadata.AttributeType == CRMDataTypes_1.AttributeTypeCode[CRMDataTypes_1.AttributeTypeCode.String] || attributeMetadata.AttributeType == CRMDataTypes_1.AttributeTypeCode[CRMDataTypes_1.AttributeTypeCode.Memo]) { attributeValue = this.ConvertToString(prop, attributes[prop]); } else if (attributeMetadata.AttributeType == CRMDataTypes_1.AttributeTypeCode[CRMDataTypes_1.AttributeTypeCode.DateTime]) { attributeValue = this.ConvertToDate(attributes[prop], attributeMetadata); } else if (attributeMetadata.AttributeType == CRMDataTypes_1.AttributeTypeCode[CRMDataTypes_1.AttributeTypeCode.Lookup] || attributeMetadata.AttributeType == CRMDataTypes_1.AttributeTypeCode[CRMDataTypes_1.AttributeTypeCode.Customer] || attributeMetadata.AttributeType == CRMDataTypes_1.AttributeTypeCode[CRMDataTypes_1.AttributeTypeCode.Owner]) { attributeValue = this.ConvertToEntityReference(attributes[prop], attributeMetadata); } else if (attributeMetadata.AttributeType == CRMDataTypes_1.AttributeTypeCode[CRMDataTypes_1.AttributeTypeCode.Picklist]) { attributeValue = this.ConvertToOptionset(attributes[prop], attributeMetadata); } else if (attributeMetadata.AttributeType == CRMDataTypes_1.AttributeTypeCode[CRMDataTypes_1.AttributeTypeCode.Boolean]) { attributeValue = this.ConvertToBoolean(attributes[prop], attributeMetadata); } else if (attributeMetadata.AttributeType == CRMDataTypes_1.AttributeTypeCode[CRMDataTypes_1.AttributeTypeCode.PartyList]) { attributeValue = this.ConvertToPartyList(attributes[prop], attributeMetadata); } else { debug("Attribute '" + prop + "' type '" + attributeMetadata.AttributeType + "' not converted. Using the raw value"); attributeValue = attributes[prop]; } } // TODO: add the rest of value types entity.Attributes[attributeMetadata.LogicalName] = attributeValue; } else { console.log("*** Attribute '" + prop + "' not found in metadata. Skipping..."); } } // TODO: Set the Entity Id debug("Converted value:"); debug(entity); return entity; }; CRMClient.prototype.ConvertToString = function (attributeName, attributeValue) { var strValue = null; if (attributeValue !== null && attributeValue !== undefined) { if (typeof attributeValue === "string") { strValue = attributeValue; } else if (attributeValue.toString !== undefined && typeof attributeValue.toString === "function") { strValue = attributeValue.toString(); } else { throw new Error("Cannot convert attribute '" + attributeName + "' value '" + attributeValue + "' from '" + typeof attributeValue + "' to 'String'"); } } return strValue; }; CRMClient.prototype.ConvertToPartyList = function (attributeValue, attributeMetadata) { var partyList = []; if (attributeValue instanceof Array) { for (var i = 0; i < attributeValue.length; i++) { var partyItem = attributeValue[i]; var partyItemRecord = { partyid: partyItem }; var convertedPartyItem = this.ConvertToEntity("activityparty", partyItemRecord); partyList.push(convertedPartyItem); } } else { throw new Error("Cannot convert attribute '" + attributeMetadata.LogicalName + "' value '" + attributeValue + "' from '" + typeof attributeValue + "' to 'ActivityParty'"); } return partyList; }; CRMClient.prototype.ConvertToDate = function (attributeValue, attributeMetadata) { var date = null; if (attributeValue instanceof Date) { date = attributeValue; } else if (typeof attributeValue == "string") { date = new Date(attributeValue); } if (date === null) throw new Error("Cannot convert attribute '" + attributeMetadata.LogicalName + "' value '" + attributeValue + "' from '" + typeof attributeValue + "' to 'Date'"); return date; }; CRMClient.prototype.ConvertToBoolean = function (attributeValue, attributeMetadata) { var boolVal = null; if (typeof attributeValue == "boolean") { boolVal = attributeValue; } else if (typeof attributeValue == "number") { if (attributeValue == 0) { boolVal = false; } else { boolVal = true; } } else if (typeof attributeValue == "string") { // Try to find the string as an Optionset Label var optionSet = attributeMetadata.OptionSet; if (optionSet.TrueOption.Label.UserLocalizedLabel.Label.toLowerCase() == attributeValue.toLowerCase() || attributeValue.toLowerCase() == "yes") { boolVal = true; } else if (optionSet.FalseOption.Label.UserLocalizedLabel.Label.toLowerCase() == attributeValue.toLowerCase() || attributeValue.toLowerCase() == "no") { boolVal = false; } } if (boolVal == null) { throw new Error("Can't convert attribute '" + attributeMetadata.LogicalName + "' value '" + attributeValue + "' from '" + typeof attributeValue + "' to Boolean"); } return boolVal; }; CRMClient.prototype.ConvertToOptionset = function (attributeValue, attributeMetadata) { var optionSet = null; if (typeof attributeValue == "number") { optionSet = new CRMDataTypes_1.OptionSetValue(attributeValue); } if (typeof attributeValue == "string") { // Try to find the string as an Optionset Label var optionSetMetadata = attributeMetadata.OptionSet; for (var i = 0; i < optionSetMetadata.Options.length; i++) { var option = optionSetMetadata.Options[i]; if (option.Label.UserLocalizedLabel.Label.toLowerCase() == attributeValue.toLowerCase()) { optionSet = new CRMDataTypes_1.OptionSetValue(option.Value); break; } } } if (optionSet == null) { throw new Error("Can't convert attribute '" + attributeMetadata.LogicalName + "' value '" + attributeValue + "' from '" + typeof attributeValue + "' to OptionsetValue"); } return optionSet; }; CRMClient.prototype.ConvertToEntityReference = function (attributeValue, attributeMetadata) { var er = null; var target = null, id = null; if (typeof attributeValue == "string") { // TODO: If the value is not a GUID, find the value in the target entity if (attributeMetadata.Targets.length > 1) throw new Error("Couldn't get a valid target for attribute '" + attributeMetadata.LogicalName + "'. Please specify a valid target using {id:string,type:string}"); target = attributeMetadata.Targets[0]; id = attributeValue; } else if (typeof attributeValue == "object") { id = attributeValue.id; target = attributeValue.type; } if (!(target && id)) throw new Error("Couldn't get a valid EntityReference value for attribute '" + attributeMetadata.LogicalName + "'. Please specify a valid target using {id:string,type:string}"); er = new CRMDataTypes_1.EntityReference(id, target); return er; }; /** * Deletes one on more records in CRM, and returns the number of records affected. * @method CRMClient#delete * @param entityName {string} Name of the entity which record you want to delete * @param idsOrConditions {string|Guid|string[]|object} Can be either a Guid, a string, an array or a conditions object. * If it is Guid will delete the record with the specified id. * If it is a string, must be a Guid value, and again, will delete the records matching the specified id. * If the parameter is an array, each element in it must be either a string or a Guid, and in each case, the records deleted will be the ones specified by these Guids. * If it is a condition object, first, all the matching records will be retrieved, and then deleted. * Learn how to write condition objects: {@link Fetch#setFilter} * @returns {number} Number of records deleted * @example <caption>Delete an account with a known Guid</caption> * var affectedRecords = crm.delete("account","6fefeb79-5447-e511-a5db-0050568a69e2"); * @example <caption>Delete an account with a known Guid. A validation of the Guid format will be performed before calling to the method.</caption> * var affectedRecords = crm.delete("account",new Guid("6fefeb79-5447-e511-a5db-0050568a69e2")); * @example <caption>Delete several account records at once</caption> * var affectedRecords = crm.delete("account",["6fefeb79-5447-e511-a5db-0050568a69e2","6fefeb79-5447-e511-a5db-0050568a69e2"); * @example <caption>Delete all existing accounts named "contoso"</caption> * var affectedRecords = crm.delete("account",{name:"contoso"}); */ CRMClient.prototype.delete = function (entityName, idsOrConditions) { var ids; var recordsAffected = 0; if (!entityName) throw new Error("Entity name not specified"); entityName = entityName.toLowerCase(); // normalize casing if (idsOrConditions instanceof Guid_1.Guid) { ids = [idsOrConditions.getValue()]; } else if (typeof idsOrConditions == "string") { if (!Guid_1.Guid.isGuid(idsOrConditions)) throw new Error("Invalid GUID value"); ids = [idsOrConditions]; } else if (Array.isArray(ids)) { for (var i = 0; i < ids.length; i++) { var item = ids[i]; if (!(item instanceof Guid_1.Guid) || Guid_1.Guid.isGuid(item)) { throw new Error("Invalid GUID"); } } ids = idsOrConditions; } else if (typeof idsOrConditions == "object" && !(idsOrConditions instanceof Date)) { // Get the records that meet the specified criteria var idField = this.getIdField(entityName); var foundRecords = this.retrieveMultiple(entityName, idsOrConditions, idField); ids = []; for (var i = 0; i < foundRecords.rows.length; i++) { ids.push(foundRecords.rows[i][idField]); } } recordsAffected = this.deleteMultiple(entityName, ids); return recordsAffected; }; CRMClient.prototype.deleteMultiple = function (entityName, ids) { var recordsAffected = 0; for (var i = 0; i < ids.length; i++) { var params = { entityName: entityName, id: ids[i] }; this._crmBridge.Delete(params, true); recordsAffected++; } return recordsAffected; }; /** * Updates one or more records that meet the specified conditions and returns the number of updated records. * @method CRMClient#update * @param entityName {string} The name of the entity which record you want to update. * @param attributes {object} Javascript object with the values the new record will have. * @param conditions {opbject} Optional. Javascript condition object with the filter values that is going to be used to know which records are going to be updated. * If you omit this parameter, then you have to provide the record GUID in the attributes parameter. * Learn how to write condition objects: {@link Fetch#setFilter} * @returns {number} Number of modified records * * @example <caption>Updates all the accounts which name is contoso, and set the attribute value to "contoso-updated"</caption> * var affectedRecords = crm.update("account",{name:"contoso-updated"},{name:"contoso"}) * @example <caption>In this example, only the account with the specified account id will be updated. If the specified record id exists, then affectedRecords will be equals to 1.</caption> * var affectedRecords = crm.update("account",{accountid:"6fefeb79-5447-e511-a5db-0050568a69e2",name:"contoso-updated"}) */ CRMClient.prototype.update = function (entityName, attributes, conditions) { var updatedRecordsCount = 0; if (!entityName) throw new Error("Entity name not specified"); entityName = entityName.toLowerCase(); // normalize casing debug("Preparing to update a record..."); // get records GUIDS if (conditions != undefined) { var idField = this.getIdField(entityName); debug("Conditions specified. Trying to find which records that meets conditions to update..."); var foundRecords = this.retrieveMultiple(entityName, conditions, idField); debug(foundRecords.rows.length + " records found"); for (var i = 0; i < foundRecords.rows.length; i++) { debug("updating " + i + " of " + foundRecords.rows.length); var foundRecordId = foundRecords.rows[i][idField]; attributes[idField] = foundRecordId; var entity = this.ConvertToEntity(entityName, attributes); this._crmBridge.Update(entity, true); } updatedRecordsCount = foundRecords.rows.length; } else { // the attributes parameter must contain the entity id on it var entity = this.ConvertToEntity(entityName, attributes); this._crmBridge.Update(entity, true); updatedRecordsCount = 1; } return updatedRecordsCount; }; CRMClient.prototype.getIdField = function (entityName) { var idAttr = null; var metadata = this.getEntityMetadata(entityName); if (metadata) { // Find the primary Attribute idAttr = metadata.PrimaryIdAttribute; } if (idAttr == null) throw new Error("Primary Attribute not found for entity " + entityName); debug("idAttr for entity '" + entityName + "': '" + idAttr + "'"); // convert it to lowercase return idAttr.toLowerCase(); }; /** Takes a list of attributes and values, tries to find an existing record with those values in CRM, if it exists, * then performs an update, otherwhise it creates it. * @method CRMClient#createOrUpdate * @param entityName {string} Name of the entity which record you want to update. * @param attributes {object} Javascript object with the attributes you want to create or update. * @param matchFields {string[]} List of fields in the attributes parameter you want to use to know if the record exists in CRM. * The attributes specified in this parameter will be used to perform a {@link CRMClient#retrieve}. * @example <caption>Create an account named "contoso". In this case, a retrieve of an account with name="contoso" will be performed. * If exists, then the name and description will be updated. If it doesn't exist, then the account will be created with the specified * name and description. If theres more than one account with that name, an exception will be thrown</caption> * crm.createOrUpdate("account",{name:"contoso", description:"Account Updated"},["name"]); * @example <caption>Searches for an account named "contoso" owned by me. If exists, it updates it, otherwhise it creates a new one.</caption> * var me = crm.whoAmI().UserId; * crm.createOrUpdate("account",{name:"contoso", description:"Account Updated", ownerid:me},["name","ownerid"]); */ CRMClient.prototype.createOrUpdate = function (entityName, attributes, matchFields) { this.createUpdate(entityName, attributes, matchFields); }; /** For every record in the specified Table, tries to find out if it does exists, and if doesn't it creates it. * @method CRMClient#createIfDoesNotExist * @param data {DataTable} DataTable with the entity name and the records to create in CRM. * @param matchFields {string[]} List of fields in the attributes parameter you want to use to know if the record exists in CRM. * The attributes specified in this parameter will be used to perform a {@link CRMClient#retrieve}. * @example <caption>Loads a list of accounts from an Excel file and creates only the ones that don't exist already in CRM. * It uses the email address to know if the account exists or not.</caption> * var accountsToLoad = DataTable.load("AccountsToLoad.xlsx"); * crm.createIfDoesNotExist(accountsToLoad,["emailaddress1"]); */ /** Takes a list of attributes and values, tries to find an existing record with those values in CRM, and if doesn't exists it creates it. * @method CRMClient#createIfDoesNotExist * @param entityName {string} Name of the entity which record you want to create. * @param attributes {object} Javascript object with the attributes you want to create. * @param matchFields {string[]} List of fields in the attributes parameter you want to use to know if the record exists in CRM. * The attributes specified in this parameter will be used to perform a {@link CRMClient#retrieve}. * @example <caption>Create an account named "contoso" only if it doesn't exist one already with the specified email. * If theres more than one account with that email, an exception will be thrown</caption> * crm.createIfDoesNotExist("account",{name:"contoso", description:"New Account Created", emailaddress1:"info@contoso.com"},["emailaddress1"]); * @example <caption>Searches for an account named "contoso" owned by me. If it doesn't exist, it creates it.</caption> * var me = crm.whoAmI().UserId; * crm.createIfDoesNotExist("account",{name:"contoso", description:"Account Updated", ownerid:me},["name","ownerid"]); */ CRMClient.prototype.createIfDoesNotExist = function (entityNameOrDataTable, attributesOrMatchfields, matchFields) { if (entityNameOrDataTable instanceof DataTable_1.DataTable) { var data = entityNameOrDataTable; if (data === undefined || data === null) throw new Error("DataTable not specified"); if (data.name === undefined || data.name === null) throw new Error("DataTable name not specified"); if (!Array.isArray(attributesOrMatchfields)) throw new Error("Wrong data type for the matchFields parameter"); var matchFields = attributesOrMatchfields; debug("About to create if does not exits " + data.rows.length + " records from a DataTable..."); for (var i = 0; i < data.rows.length; i++) { debug(i + " of " + data.rows.length + "..."); this.createUpdate(data.name, data.rows[i], matchFields, false); } } else { if (matchFields === undefined || matchFields === null) throw new Error("matchFields not specified"); this.createUpdate(entityNameOrDataTable, attributesOrMatchfields, matchFields, false); } }; CRMClient.prototype.createUpdate = function (entityName, attributes, matchFields, update) { if (update === void 0) { update = true; } var idField = this.getIdField(entityName); var conditions = {}; debug("About to create or update a record..."); for (var i = 0; i < matchFields.length; i++) { var matchField = matchFields[i]; debug("Preparing query to know if the record exists..."); var attr = MetadataUtil_1.MetadataUtil.getAttributeMetadata(this, entityName, matchField); if (attr === null) throw new Error("Attribute '" + matchField + "' not found in entity " + entityName); if (attributes[matchField] !== undefined && attributes[matchField] !== null) { conditions[attr.LogicalName] = attributes[matchField]; } } // check if the record exists var foundRecords = this.retrieveMultiple(entityName, conditions, idField); if (foundRecords && foundRecords.rows.length > 0) { debug(foundRecords.rows.length + " '" + entityName + "' records found"); if (update) { if (foundRecords.rows.length > 1) throw new Error("Too many records found"); var foundRecord = foundRecords.rows[0]; attributes[idField] = foundRecord[idField]; this.update(entityName, attributes); } } else { debug("The record doesn't exists. Create it"); this.create(entityName, attributes); } }; /** */ CRMClient.prototype.associateData = function (data) { for (var i = 0; i < data.rows.length; i++) { var row = data.rows[i]; this.associate(row.from.type, row.from.value, data.name, row.to.type, row.to.value); } }; CRMClient.prototype.associate = function (fromEntityName, fromEntityId, relationshipName, toEntityName, toEntityId) { // perform some validations if (!fromEntityName) throw new Error("From entity name not specified"); fromEntityName = fromEntityName.toLowerCase(); // normalize casing if (!toEntityName) throw new Error("To entity name not specified"); toEntityName = toEntityName.toLowerCase(); // normalize casing if (!fromEntityId) throw new Error("fromEntityId not specified"); if (!toEntityId) throw new Error("toEntityId not specified"); var fromId, toId; if (fromEntityId instanceof Guid_1.Guid) { fromId = fromEntityId.getValue(); } else { fromId = fromEntityId; } if (toEntityId instanceof Guid_1.Guid) { toId = toEntityId.getValue(); } else { toId = toEntityId; } var params = { entityName: fromEntityName, entityId: fromId, relationship: relationshipName, relatedEntities: [{ entityName: toEntityName, entityId: toId }] }; this._crmBridge.Associate(params, true); }; CRMClient.prototype.disassociateData = function (data) { for (var i = 0; i < data.rows.length; i++) { var row = data.rows[i]; this.disassociate(row.from.type, row.from.value, data.name, row.to.type, row.to.value); } }; CRMClient.prototype.disassociate = function (fromEntityName, fromEntityId, relationshipName, toEntityName, toEntityId) { // perform some validations if (!fromEntityName) throw new Error("From entity name not specified"); fromEntityName = fromEntityName.toLowerCase(); // normalize casing if (!toEntityName) throw new Error("To entity name not specified"); toEntityName = toEntityName.toLowerCase(); // normalize casing if (!fromEntityId) throw new Error("fromEntityId not specified"); if (!toEntityId) throw new Error("toEntityId not specified"); var fromId, toId; if (fromEntityId instanceof Guid_1.Guid) { fromId = fromEntityId.getValue(); } else { fromId = fromEntityId; } if (toEntityId instanceof Guid_1.Guid) { toId = toEntityId.getValue(); } else { toId = toEntityId; } var params = { entityName: fromEntityName, entityId: fromId, relationship: relationshipName, relatedEntities: [{ entityName: toEntityName, entityId: toId }] }; this._crmBridge.Disassociate(params, true); }; /** Gets metadata information for a particular CRM Entity. * @method CRMClient#getEntityMetadata * @param entityName {string} Name of the entity which metadata information you want to get * @example <caption>Retrieve metadata information of the account entity</caption> * var metadata = crm.getEntityMetadata("account"); */ CRMClient.prototype.getEntityMetadata = function (entityName) { return MetadataUtil_1.MetadataUtil.getEntityMetadata(this, entityName); }; CRMClient.prototype.Execute = function (request) { var response = this._crmBridge.Execute(request, true); return response; }; CRMClient.prototype.assign = function (targetId, targetType, assigneeId, assigneeType) { // set the default value if (assigneeType === undefined) assigneeType = "systemuser"; var request = new Messages_1.AssignRequest(); request.Assignee = new CRMDataTypes_1.EntityReference(assigneeId.toString(), assigneeType); request.Target = new CRMDataTypes_1.EntityReference(targetId.toString(), targetType); var response = this.Execute(request); }; CRMClient.prototype.export = function (entityName, fileName) { ImportExportUtil_1.ImportExportUtil.export(this, entityName, fileName); }; CRMClient.prototype.import = function (fileName) { ImportExportUtil_1.ImportExportUtil.import(this, fileName); }; /** Sets the state and status of a record. * @method CRMClient#setState * @param entityName {string} Name of the entity which state or status you want to set * @param entityId {Guid|string} GUID of the record which state or status you want to set * @param state {number|string} Name or Value of the State you want to set * @param status {number|string} Name or Value of the Status you want to set * @example <caption>Set the state of a task to Completed (1) and the Status to Completed (5)</caption> * crm.setState('task','6fefeb79-5447-e511-a5db-0050568a69e2',1,5); * @example <caption>Set the state of a task using the text values</caption> * crm.setState('task','6fefeb79-5447-e511-a5db-0050568a69e2','Completed','Completed'); * */ CRMClient.prototype.setState = function (entityName, entityId, state, status) { StateUtil_1.StateUtil.setState(this, entityName, entityId, state, status); }; return CRMClient; }()); exports.CRMClient = CRMClient; //# sourceMappingURL=CRMClient.js.map