"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