Following Part 1 and Part 2 here is how to "Update" and "Delete" using the new CRM WebAPI.
this.updateRecord = function (id, object, entitySetName, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("PATCH", encodeURI(getWebAPIPath() + type + "(" + id + ")"), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204 || this.status == 1223) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
req.send(JSON.stringify(object));
};
this.deleteRecord = function (id, entitySetName, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("DELETE", encodeURI(getWebAPIPath() + type + "(" + id + ")", true));
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204) {
successCallback();
}
else {
errorCallback(bd_Utilities.WebAPI.errorHandler(this));
}
}
};
req.send();
};
A Blog about my experience with Microsoft Dynamics CRM as developer.
Featured Post
Web API Requests Series
Web API Series: Create and Retrieve , Update and Delete , Retrieve Multiple , Associate/Disassociate , Impersonation , Run Workflows
02 March 2016
CRM 2016 - WEB API CRUD Operations (Part 2)
Retrieving multiple records using paging with the new CRM 2016 Web API in Javascript
On my previous post I talked about how to "Retrieve" a single record by GUID and how to create a record.
In this post I will show you how to perform a simple "RETRIEVEMULTIPLE" using the new CRM Web API.
Straight to the code, which you can add to the other functions from my previous post.
The highlighted code is necessary to achieve paging in cases where we want to, for whatever reason, or when there are more than 5000 records in the result set.
(Note that is different from the previous implementation of the OData service)
If you do not want to do paging and you have more than 5000 records remove this line:
"req.setRequestHeader("Prefer", "odata.maxpagesize=500");" and every page will contain 5000 records not 50 as in the previous OData service.
this.retrieveMultipleRecords = function (entitySetName, select, successCallback, errorCallback, onComplete) {
var req = new XMLHttpRequest();
//This part works, but I am not sure if it is the best way
if (select.indexOf("skiptoken") != -1) {
req.open("GET", select, true);
}
else {
req.open("GET", encodeURI(getWebAPIPath() + entitySetName + select), true);
}
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Prefer", "odata.maxpagesize=500");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200) {
var returned = JSON.parse(this.responseText, bd_Utilities.WebAPI.dateReviver);
if (successCallback) {
successCallback(returned);
}
if (returned["@odata.nextLink"] != null) {
var queryOptions = returned["@odata.nextLink"];
bd_Utilities.WebAPI.retrieveMultipleRecords(entitySetName, queryOptions, successCallback, errorCallback, onComplete);
}
else {
onComplete();
}
}
else {
if (errorCallback) {
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
}
};
req.send();
};
And here is the function call:
retrieveMultiple: function () {
"use strict";
bd_Utilities.WebAPI.retrieveMultipleRecords("new_events", "?$select=new_name",
function (results) {
events = events.concat(results.value);
},
function (error) { console.log(error.message); },
function () {
console.log("Retrieve multiple complete!");
});
},
01 March 2016
CRM 2016 - WEB API CRUD Operations (Part 1)
How to CRUD with the new CRM 2016 Web API in Javascript
In this post I wanted to talk of the new Web API available in CRM 2016. I am not sure about you but I could not find any example, other than the basic Create operation provided by Microsoft, of how to use the new Web API for CRUD operations.
So here it is an example of a RETRIEVE (and the CREATE from the Microsoft example found here) query using the Web API, I will be adding more examples of "Retrieve Multiple, "Update" and "Delete" as I discover how to implement them.
"use strict";
var bd_Utilities = window.bd_Utilities || {};
bd_Utilities.WebAPI = bd_Utilities.WebAPI || {};
(function () {
this.create = function (entitySetName, entity, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("POST", encodeURI(getWebAPIPath() + entitySetName), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204) {
if (successCallback)
successCallback(this.getResponseHeader("OData-EntityId"));
}
else {
if (errorCallback)
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
};
req.send(JSON.stringify(entity));
};
this.retrieve = function (id, entitySetName, select, expand, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("GET", encodeURI(getWebAPIPath() + entitySetName + "(" + id + ")" + select), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200) {
if (successCallback)
successCallback(JSON.parse(this.responseText, bd_Utilities.WebAPI.dateReviver));
}
else {
if (errorCallback)
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
};
req.send();
};
//This is the calling function:
search: function () {
bd_Utilities.WebAPI.retrieve("00000000-0000-0000-0000-000000000090", "contacts", "?$select=firstname,lastname", "",
function (result) {
var contact = result;
console.log("Contact retrieved")
},
function (error) { console.log(error.message); });
}
//And these are some helper functions from the CRM SDK
//Internal supporting functions
function getClientUrl() {
//Get the organization URL
if (typeof GetGlobalContext == "function" &&
typeof GetGlobalContext().getClientUrl == "function") {
return GetGlobalContext().getClientUrl();
}
else {
//If GetGlobalContext is not defined check for Xrm.Page.context;
if (typeof Xrm != "undefined" &&
typeof Xrm.Page != "undefined" &&
typeof Xrm.Page.context != "undefined" &&
typeof Xrm.Page.context.getClientUrl == "function") {
try {
return Xrm.Page.context.getClientUrl();
} catch (e) {
throw new Error("Xrm.Page.context.getClientUrl is not available.");
}
}
else { throw new Error("Context is not available."); }
}
}
function getWebAPIPath() {
return getClientUrl() + "/api/data/v8.0/";
}
//Internal validation functions
function isString(obj) {
if (typeof obj === "string") {
return true;
}
return false;
}
function isNull(obj) {
if (obj === null)
{ return true; }
return false;
}
function isUndefined(obj) {
if (typeof obj === "undefined") {
return true;
}
return false;
}
function isFunction(obj) {
if (typeof obj === "function") {
return true;
}
return false;
}
function isNullOrUndefined(obj) {
if (isNull(obj) || isUndefined(obj)) {
return true;
}
return false;
}
function isFunctionOrNull(obj) {
if (isNull(obj))
{ return true; }
if (isFunction(obj))
{ return true; }
return false;
}
// This function is called when an error callback parses the JSON response
// It is a public function because the error callback occurs within the onreadystatechange
// event handler and an internal function would not be in scope.
this.errorHandler = function (resp) {
try {
return JSON.parse(resp).error;
} catch (e) {
return new Error("Unexpected Error")
}
}
this.dateReviver = function (key, value) {
///<summary>
/// Private function to convert matching string values to Date objects.
///</summary>
///<param name="key" type="String">
/// The key used to identify the object property
///</param>
///<param name="value" type="String">
/// The string value representing a date
///</param>
var a;
if (typeof value === 'string') {
a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));
}
}
return value;
}
}).call(bd_Utilities.WebAPI);
In this post I wanted to talk of the new Web API available in CRM 2016. I am not sure about you but I could not find any example, other than the basic Create operation provided by Microsoft, of how to use the new Web API for CRUD operations.
So here it is an example of a RETRIEVE (and the CREATE from the Microsoft example found here) query using the Web API, I will be adding more examples of "Retrieve Multiple, "Update" and "Delete" as I discover how to implement them.
"use strict";
var bd_Utilities = window.bd_Utilities || {};
bd_Utilities.WebAPI = bd_Utilities.WebAPI || {};
(function () {
this.create = function (entitySetName, entity, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("POST", encodeURI(getWebAPIPath() + entitySetName), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 204) {
if (successCallback)
successCallback(this.getResponseHeader("OData-EntityId"));
}
else {
if (errorCallback)
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
};
req.send(JSON.stringify(entity));
};
this.retrieve = function (id, entitySetName, select, expand, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("GET", encodeURI(getWebAPIPath() + entitySetName + "(" + id + ")" + select), true);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200) {
if (successCallback)
successCallback(JSON.parse(this.responseText, bd_Utilities.WebAPI.dateReviver));
}
else {
if (errorCallback)
errorCallback(bd_Utilities.WebAPI.errorHandler(this.response));
}
}
};
req.send();
};
//This is the calling function:
search: function () {
bd_Utilities.WebAPI.retrieve("00000000-0000-0000-0000-000000000090", "contacts", "?$select=firstname,lastname", "",
function (result) {
var contact = result;
console.log("Contact retrieved")
},
function (error) { console.log(error.message); });
}
//And these are some helper functions from the CRM SDK
//Internal supporting functions
function getClientUrl() {
//Get the organization URL
if (typeof GetGlobalContext == "function" &&
typeof GetGlobalContext().getClientUrl == "function") {
return GetGlobalContext().getClientUrl();
}
else {
//If GetGlobalContext is not defined check for Xrm.Page.context;
if (typeof Xrm != "undefined" &&
typeof Xrm.Page != "undefined" &&
typeof Xrm.Page.context != "undefined" &&
typeof Xrm.Page.context.getClientUrl == "function") {
try {
return Xrm.Page.context.getClientUrl();
} catch (e) {
throw new Error("Xrm.Page.context.getClientUrl is not available.");
}
}
else { throw new Error("Context is not available."); }
}
}
function getWebAPIPath() {
return getClientUrl() + "/api/data/v8.0/";
}
//Internal validation functions
function isString(obj) {
if (typeof obj === "string") {
return true;
}
return false;
}
function isNull(obj) {
if (obj === null)
{ return true; }
return false;
}
function isUndefined(obj) {
if (typeof obj === "undefined") {
return true;
}
return false;
}
function isFunction(obj) {
if (typeof obj === "function") {
return true;
}
return false;
}
function isNullOrUndefined(obj) {
if (isNull(obj) || isUndefined(obj)) {
return true;
}
return false;
}
function isFunctionOrNull(obj) {
if (isNull(obj))
{ return true; }
if (isFunction(obj))
{ return true; }
return false;
}
// This function is called when an error callback parses the JSON response
// It is a public function because the error callback occurs within the onreadystatechange
// event handler and an internal function would not be in scope.
this.errorHandler = function (resp) {
try {
return JSON.parse(resp).error;
} catch (e) {
return new Error("Unexpected Error")
}
}
this.dateReviver = function (key, value) {
///<summary>
/// Private function to convert matching string values to Date objects.
///</summary>
///<param name="key" type="String">
/// The key used to identify the object property
///</param>
///<param name="value" type="String">
/// The string value representing a date
///</param>
var a;
if (typeof value === 'string') {
a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));
}
}
return value;
}
}).call(bd_Utilities.WebAPI);
Labels:
2015,
crm 2016,
crm 2016 online,
CRUD,
examples,
luciano d'angelo,
luciano dangelo,
odata,
odata 4.0,
online,
REST,
retrieve multiple,
sample code,
web api,
web api create,
web api javascript,
web api retrieve
25 February 2015
How to use the ExecuteMultipleRequest to resolve cases
This post is about how to use the "ExecuteMultipleRequest" message to resolve cases in Bulk.
The main advantage is that if you have to update thousands of record you can do that by sending one single server request and not thousands of requests as it happens with the single Request.
This particular example is for changing the state and status of cases. To achieve that you have to use the "CloseIncidentRequest" message.
The "ExecuteMultipleRequest" message allows us to create a collection of "CloseIncidentRequest" requests and send it to the server to be processed.
Let's start off by creating a "ExecuteMultipleRequest" object:
var myMultipleRequest = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = true,
ReturnResponses = false
},
Requests = new OrganizationRequestCollection()
};
And then we can loop through our list of cases to create a "CloseIncidentRequest" for each case.
I used a helper method to build the CloseIncidentRequest called "BuildIncidentResolutionRequest"
which takes the incident id and the status reason as input:
foreach (var incident in cases.Entities)
{
var closeIncidentRequest = BuildIncidentResolutionRequest(incident.Id, caseStatusReason);
myMultipleRequest.Requests.Add(closeIncidentRequest);
}
This method first creates a new Entity (which is an activity of type Case Resolution) called "incidentresolution" which will be used in the "CloseIncidentRequest" this entity is used to resolve the incident and a subject and description can be provided.
Note: If you try to resolve a case using the UI you must enter a resolution (which is the subject) that information will be held in the entity called "incidentresolution" which can be viewed in CRM with an advanced find or in the closed activities linked to the case. An "incidentresolution" record will be created for each case you are resolving.
It then creates the "CloseIncidentRequest".
private CloseIncidentRequest BuildIncidentResolutionRequest(Guid incidentId, int caseStatusReason)
{
// Create the incident's resolution.
var incidentResolution = new Entity("incidentresolution");
incidentResolution["subject"] = "Case was Resolved";
incidentResolution["incidentid"] = new EntityReference("incident", incidentId);
incidentResolution["description"] = "Description";
// Close the incident with the resolution.
var closeIncidentRequest = new CloseIncidentRequest
{
IncidentResolution = incidentResolution,
Status = new OptionSetValue(caseStatusReason)
};
return closeIncidentRequest;
}
...and finally the list of requests is sent to the server to be processed.
var responses = (ExecuteMultipleResponse)orgService.Execute(myMultipleRequest);
We can also assert if any error occurred by checking the "IsFaulted" flag.
Anyway for more information refer to the msdn this page.
if (responses.IsFaulted)
{
Console.WriteLine("Error: {0}", myMultipleRequest.Responses[0].Fault.Message);
}
The main advantage is that if you have to update thousands of record you can do that by sending one single server request and not thousands of requests as it happens with the single Request.
This particular example is for changing the state and status of cases. To achieve that you have to use the "CloseIncidentRequest" message.
The "ExecuteMultipleRequest" message allows us to create a collection of "CloseIncidentRequest" requests and send it to the server to be processed.
Let's start off by creating a "ExecuteMultipleRequest" object:
var myMultipleRequest = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = true,
ReturnResponses = false
},
Requests = new OrganizationRequestCollection()
};
And then we can loop through our list of cases to create a "CloseIncidentRequest" for each case.
I used a helper method to build the CloseIncidentRequest called "BuildIncidentResolutionRequest"
which takes the incident id and the status reason as input:
foreach (var incident in cases.Entities)
{
var closeIncidentRequest = BuildIncidentResolutionRequest(incident.Id, caseStatusReason);
myMultipleRequest.Requests.Add(closeIncidentRequest);
}
This method first creates a new Entity (which is an activity of type Case Resolution) called "incidentresolution" which will be used in the "CloseIncidentRequest" this entity is used to resolve the incident and a subject and description can be provided.
Note: If you try to resolve a case using the UI you must enter a resolution (which is the subject) that information will be held in the entity called "incidentresolution" which can be viewed in CRM with an advanced find or in the closed activities linked to the case. An "incidentresolution" record will be created for each case you are resolving.
It then creates the "CloseIncidentRequest".
private CloseIncidentRequest BuildIncidentResolutionRequest(Guid incidentId, int caseStatusReason)
{
// Create the incident's resolution.
var incidentResolution = new Entity("incidentresolution");
incidentResolution["subject"] = "Case was Resolved";
incidentResolution["incidentid"] = new EntityReference("incident", incidentId);
incidentResolution["description"] = "Description";
// Close the incident with the resolution.
var closeIncidentRequest = new CloseIncidentRequest
{
IncidentResolution = incidentResolution,
Status = new OptionSetValue(caseStatusReason)
};
return closeIncidentRequest;
}
...and finally the list of requests is sent to the server to be processed.
var responses = (ExecuteMultipleResponse)orgService.Execute(myMultipleRequest);
We can also assert if any error occurred by checking the "IsFaulted" flag.
Anyway for more information refer to the msdn this page.
if (responses.IsFaulted)
{
Console.WriteLine("Error: {0}", myMultipleRequest.Responses[0].Fault.Message);
}
04 July 2012
How to associate a record as Activity Party to an activity.
In this post I will show you how to associate a record to an activity (in this case an email) as an Activity Party using the OData REST end point with Javascript.
Setting the "To", "From", "Cc", "Bcc" fields on an email message is not so straight forward as other lookup fields such as the "Regarding" field. This is because these fields are of Activity Party type.
There are two important steps to follow:
Use: activityParty.ParticipationTypeMask = { Value: 1 }; To set the "From" field
Use: activityParty.ParticipationTypeMask = { Value: 2 }; To set the "To" field
Use: activityParty.ParticipationTypeMask = { Value: 3 }; To set the "Cc" field
Use: activityParty.ParticipationTypeMask = { Value: 4 }; To set the "Bcc" fiel.
Ultimately perform the REST request.
Here is the complete code:
createActivityParty: function (object, email, logicalName, recipient) {
var activityParty = {}, jsonActivityParty;
switch (logicalName) {
case "account":
activityParty.PartyId = {
Id: object.AccountId,
LogicalName: "account"
};
break;
case "contact":
activityParty.PartyId = {
Id: object.ContactId,
LogicalName: "contact"
};
break;
case "systemUser":
activityParty.PartyId = {
Id: object.SystemUserId,
LogicalName: "systemuser"
};
break;
default:
}
// Set the "activity" of the ActivityParty (the e-mail, in this case) as an EntityReference.
activityParty.ActivityId = {
Id: email.ActivityId,
LogicalName: "email"
};
// Set the participation type (what role the party has on the activity). For this
// example, we'll put the account in the From field (which has a value of 1).
if (recipient == "sender") {
activityParty.ParticipationTypeMask = { Value: 1 };
}
else if (recipient == "to") {
activityParty.ParticipationTypeMask = { Value: 2 };
}
else if (recipient == "cc") {
activityParty.ParticipationTypeMask = { Value: 3 };
}
//stringify the object to be created
jsonActivityParty = JSON.stringify(activityParty);
this._performRequest({
type: "POST",
url: this._ODataPath() + "/ActivityPartySet",
data: jsonActivityParty,
success: function (request) {
},
error: function (request) {
this._errorHandler(request);
}
});
},
Hope this helps, also don't forget to check the SDK. which is where I learned about this.
Setting the "To", "From", "Cc", "Bcc" fields on an email message is not so straight forward as other lookup fields such as the "Regarding" field. This is because these fields are of Activity Party type.
There are two important steps to follow:
- Set the "party" of the ActivityParty (what will be related to the activity) as an EntityReference. activityParty.PartyId = { Id: record.RecordId, LogicalName: "logicalName" };
- Set the "activity" of the ActivityParty (the e-mail, in this case) as an EntityReference. activityParty.ActivityId = { Id: email.ActivityId, LogicalName: "email" };
Use: activityParty.ParticipationTypeMask = { Value: 1 }; To set the "From" field
Use: activityParty.ParticipationTypeMask = { Value: 2 }; To set the "To" field
Use: activityParty.ParticipationTypeMask = { Value: 3 }; To set the "Cc" field
Use: activityParty.ParticipationTypeMask = { Value: 4 }; To set the "Bcc" fiel.
Ultimately perform the REST request.
Here is the complete code:
createActivityParty: function (object, email, logicalName, recipient) {
var activityParty = {}, jsonActivityParty;
switch (logicalName) {
case "account":
activityParty.PartyId = {
Id: object.AccountId,
LogicalName: "account"
};
break;
case "contact":
activityParty.PartyId = {
Id: object.ContactId,
LogicalName: "contact"
};
break;
case "systemUser":
activityParty.PartyId = {
Id: object.SystemUserId,
LogicalName: "systemuser"
};
break;
default:
}
// Set the "activity" of the ActivityParty (the e-mail, in this case) as an EntityReference.
activityParty.ActivityId = {
Id: email.ActivityId,
LogicalName: "email"
};
// Set the participation type (what role the party has on the activity). For this
// example, we'll put the account in the From field (which has a value of 1).
if (recipient == "sender") {
activityParty.ParticipationTypeMask = { Value: 1 };
}
else if (recipient == "to") {
activityParty.ParticipationTypeMask = { Value: 2 };
}
else if (recipient == "cc") {
activityParty.ParticipationTypeMask = { Value: 3 };
}
//stringify the object to be created
jsonActivityParty = JSON.stringify(activityParty);
this._performRequest({
type: "POST",
url: this._ODataPath() + "/ActivityPartySet",
data: jsonActivityParty,
success: function (request) {
},
error: function (request) {
this._errorHandler(request);
}
});
},
Hope this helps, also don't forget to check the SDK. which is where I learned about this.
Subscribe to:
Posts (Atom)