sData

SData in SalesLogix 7.5.2 — PUT and DELETE

Posted by Jason Huber on October 16, 2009
Developer / No Comments

We have already learned how about the verbs: PUT, DELETE, POST and GET, and we have exposure to Basic and Digest Authentication, so now we need to actually PUT or DELETE something.

PUT and DELETE in this context are used as the method on your http request. Each HTTP request has a method if you did not already realize it. If you look at the IIS logs for any IIS server you will see many POST and Get requests because those verbs are normally associated with web browsing. The SData team and based on, I am guessing, the RESTful web services guys recommendation that certain verbs be used when accessing RESTful resources over http (not your typical web browsing).

PUT is to tell SData that you are Updating and DELETE is to tell SData that you are deleting. That is just fine. So how can you actually do the telling? First we know how to do a get on any resource:

System.Net.WebClient client = new System.Net.WebClient();
client.Encoding = Encoding.UTF8;
client.Headers.Add(System.Net.HttpRequestHeader.ContentType, "application/atom+xml");
client.Credentials = new System.Net.NetworkCredential(_User, _Password);

string result = client.DownloadString("http://localhost:3333/sdata/slx/dynamic/-/Tickets");

where the url is the RESTful end point for all tickets. This will return a feed of Tickets. If you wanted just a single ticket you could:
string result = client.DownloadString("http://localhost:3333/sdata/slx/dynamic/-/Tickets('123123123')");

where TicketId is 123123123.

You can also perform searches like

string result = client.DownloadString("http://localhost:3333/sdata/slx/dynamic/-/Tickets?where=ticketid eq '123123123'");

and so on. Documentation for the Query Expression is forthcoming and remind me to update this if 7.5.2 is out and you do not yet have it (I will add a link).

The Ticket comes back in the result and has a root node of feed (it is all xml). Within the feed are various items including an entry which contains the actual ticket and the tickets payload. To retrieve that we can do something like :
(snippet of code – this is well documented in the developers subscription) :

XPathNodeIterator nav = xmlNav.Select("//slx:Ticket"manager);
nav.MoveNext();
return nav.Current.InnerXml;

Now we have an xml file (XmlDocument type) and it contains the values for the Ticket we selected. We wanted to update something. Each value will be returned as a node:
SDataPost_delete_Nodes

You need to get one of these nodes. Here is the code to get back the Area:

You need to know that passed in is a valid xml document (xdoc and the node you want: “//slx:Area”)

var manager = new XmlNamespaceManager(xdoc.NameTable);
XmlNamespaceManagerAddNamespacesforslx(ref manager);

XmlNode xnode = xdoc.SelectSingleNode(sNodeName, manager);
return xnode.InnerText;

But this will not update the value, it will only return it. We need to get something to update.
(this code was note written by me, but only edited):
private void UpdateSingleValue(string nodename, string nodevalue, ref XmlDocument doc)
{
var xmlNav = doc.DocumentElement.CreateNavigator();
var manager = new XmlNamespaceManager(doc.NameTable);
XmlNamespaceManagerAddNamespacesforslx(ref manager);

var nav = xmlNav.SelectSingleNode(nodename, manager);
if (nav.Value != nodevalue)
{
var nil = nav.SelectSingleNode("@xsi:nil", manager);
if (nodevalue == null)
{
if (nil == null)
{
var attributes = nav.CreateAttributes();
attributes.WriteAttributeString("xsi:nil", "true");
attributes.Close();
}
}
else
{
if (nil != null)
{
nil.DeleteSelf();
}
}

nav.SetValue(nodevalue);
}
else
{
nav.DeleteSelf();
}
}

Once we have an xml doc containing and entry and a payload node we can tell it to go back to the server and update right? Not so fast. We still need to pass in an if-match header. This ensures that the record has not been updated since we last retrieved it. How do we get the if-match header value? It is actually in the entry node in a node called etag:

SDataPost_delete_etag

We need to get the etag (like we did for the single value) and this time we use it. We take the etag and add it to the request headers as the if-match and PUT our data. Our response should be 200 and the database should be updated:

private bool SendUpdatedEntryViaPUT(XmlDocument _xmlCurrent, string url)
{
Uri uri = new Uri(url);
System.Net.HttpWebRequest request = null;
request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "PUT";
request.ContentType = "application/atom+xml;type=entry";
request.Headers.Add("if-match", GetSingleValueFromxmldoc(_xmlCurrent, "//http:etag"));

UTF8Encoding encoding = new UTF8Encoding();
byte[] postBytes = encoding.GetBytes(_xmlCurrent.InnerXml);
request.ContentLength = postBytes.Length;

System.IO.Stream postStream = request.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Close();

request.PreAuthenticate = true;
request.Credentials = new NetworkCredential(_User, _Password);
string result = string.Empty;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8))
{
result = readStream.ReadToEnd();
//MessageBox.Show(result);
}
}
}
return true;
}

What about DELETE? That is the easy part. For DELETE we should not need the etag and change the method to DELETE.
That’s it!

Tags: , , , , ,

SData in SalesLogix web 7.5.2 Introduction

Posted by Jason Huber on October 13, 2009
Developer / 2 Comments

There are a few changes to SData in 7.5.2, but if you are already writing code against SData the only thing you will really need to be concerned about is the new Payload Node. It is a new node within the entry and contains most of what you are looking for. This is the only real breaking change that has caused me to rewrite any code.
SDataPayload

There are some benefits such as Batch Mode, Basic authentication, and a few others, but let’s make sure we know the pieces first. We will need to cover $template, $schema, POST, GET, DELETE, PUT, Basic, Digest, and probably a few more things. In this post we will just define each one and tell you what it is used for (or what I use it for – there is always another use).

One of the first things I wanted to do when I started to look at SData from JavaScript was to get back some data. I wanted to either see what an entity was made of ($schema or $template) or actually get back an actually Account (GET). The trouble is previously authentication with SData was done only via Digest authentication. Digest works this way:

  1. You request a resource (you send a POST to a page or perhaps /Accounts/$template)
  2. You are rejected by the web server with a 400 error and also sent back a “nonce”
  3. The nonce is really a string you use to MD5 your username and password
  4. You MD5 your username and password and repeat the request
  5. You are authenticated for this request and you can continue

JavaScript does not like this process so well. Not only it is not something supported by a JavaScript Library like jQuery, but it is also hard to manage the state when the request comes back and then needs to be posted again. You end up with a triple request for each verb you need to send. Ugly.

Enter basic authentication. Basic authentication is like Digest in that the information is passed over http, but in Basic your username is simply base64 encoded. At this point the security risk flags go up. Basic is less secure than Digest! While that is true for the initial username and password, think about all the actual data you are passing after authenticated! None of that is encrypted in either case. So we suggest https. Simple as that.

Back to Basic authentication. With Basic authentication I can base64 my username and password and pass that as a header along with my request. Simple as that. I need to edit the headers of my request anyway, so this is a piece of cake for authentication. Check out the screenshots for Digest in C# and Basic in JavaScript:
SDataBasicAuthJavaScriptSDataDigestAuthCSharp

Now that we are authenticated we can do a few things. We can get the $template for an entity. This gives us the outline of the object that we would use to POST (insert) a new entity. We do this by issuing the following GET:
“http://localhost:3333/sdata/slx/dynamic/-/” + _entity + “/$template”

You get the point. It is just the entity/$template. What we get back is an ATOM feed with a Payload of an empty version of that entity. You edit the XML and add the values you want and then send it back with a POST and a record will be inserted.

What about POST, GET, PUT and DELETE? These are RESTful verbs. Used in the actual command (request) sent to SData they tell SData what action you want to take:

  1. POST – Insert a new record
  2. PUT – Update a record (needs an etag added for if-match
  3. Delete – Deletes a record (also needs the etag on it)
  4. Get – select or read

So what if you do not know the make-up of an entity? What if you do not know the $schema? You can go GET it:

“http://localhost:3333/sdata/slx/dynamic/-/$schema”

Why did I not do:
“http://localhost:3333/sdata/slx/dynamic/-/Accounts/$schema” ? Because according to the spec that just redirects you to: “http://localhost:3333/sdata/slx/dynamic/-/$schema” with a #Accounts on the end (pretty much).

The problem here is that $schema returns all of the entities. This means that you need to find the node you want and loop through the nodes within that entity. Here is some sample JavaScript:

x=xml.getElementsByTagName("xs:complexType")
for (i=0;i {
if(x[i].getAttribute("name") ==_singularentity + "--type")
{

y = x[i].childNodes[0].childNodes;
sappend +="<table width='500 px'>";
sappend +="<tr><td>Field Name</td><td>Type</td><td>Nullable?</td></tr>";
for (j=0;j {
sappend += "<tr><td width='300 px'>" + y[j].getAttribute("name") + "</td><td>";
sappend += y[j].getAttribute("type");
sappend += "</td>";
sappend += "<td>";
sappend += y[j].getAttribute("nillable");
sappend += "</td></tr>";

}

So far we can get the $template, $schema, and we understand the verbs. How do we then PUT an update or DELETE a record? Well we will take a look at that in the next post.

Tags: , , , , , ,

Use FireBug to help in your client-side development in SalesLogix Web

Posted by Jason Huber on October 05, 2009
Developer / No Comments

What is Firebug?

Firebug is a free add-on for Mozilla FireFox. It can be installed from https://addons.mozilla.org/en-US/firefox/addon/1843. Once it is installed it is launched by clicking the little “bug” in the lower right corner of FireFox. The bug will turn Orange to indicate it is enabled. Here you can see it turned “off”: UseFireBug_firebugoff

Why Firebug?

What can FireBug do for you that will help with your development? The most obvious feature is that is allows you to inspect the HTML code. While in this mode you hover over items on the webpage you are viewing and the code the generated these User Interface items will be shown. This is helpful when trying to diagnose misplaced elements on a page or when looking for the Cascading Style Sheet class that a particular UI element implements.
UseFireBug_firebugtabs
You can edit the Cascading Style sheets inline. By clicking the CSS tab at the top of the firebug window you can actually edit the CSS and make changes. These changes will be immediately reflected in the browser window.

UseFireBug_NETThe NET option allows you to see how long each request took to perform and the details of that request. This allows you to see all POST, GET, and other NET request sent down the http pipeline. You can tell what is being passed and how long it is taking. What is really important to me right now is being able to see the request and response headers. This tells me the authentication method I am using for my SData work in JavaScript.

The script tab helps if you need to debug your JS code. You can step through your code by placing a breakpoint to the left of the line of code that you want to break on. You can step through the code using the menu options in the upper right of the firebug toolbar.UseFireBug_script

How have I found it to be most useful?

When working with ATOM result sets like those received from a GET to an SData feed Firebug allows you to look right into the resulting XML. This makes traversing the DOM a bit easier since you can see where you are at in the structure. In previous days we would alert or print the elements while looping which was time consuming to say the least.
Internet Explorer 8 ships with a set of developer tools as well which rival what we use firebug for nearly every day. I guess since the days of the javascript: console in Mozilla it was hard to not use FireFox when it comes to client side code. Perhaps I will take a look at the IE tools in a future post.

Tags: , , , , ,

Setting up SalesLogix sData and Process Orchestration – explained

Posted by Jason Huber on September 04, 2009
Developer / No Comments

So we want to setup sData and Process Orchestration and reading the installation guides we see that we need to change some settings in IIS and an additional setting in the SalesLogix Administrator.

But what are we actually changing here and why?

sDataandPO_IISSettings1
First we need to ensure that all requests, not already mapped are sent to the isapi.dll. Why? Because IIS by default will only “handle” or do something with requests that it understands. sData sends in requests that are not understandable by IIS 6.0 out of the box. Stuff like: http://localhost:3333/sdata/slx/dynamic/-/accounts is not something IIS understands out of the box. IIS does understand http://localhost:3333/slxclient/accounts.aspx (notice the .aspx) in the file name.

So we tell IIS to handle all other request (wildcards) using the Isapi filter. We tell it to NOT check to see if the file exists. We aren’t checking to see if the isapi.dll exists, we are checking to see if /accounts exists as in http://localhost:3333/sdata/slx/dynamic/-/accounts which is does not. If you are wondering about the reason for the interesting link then you should check out RESTful web services.

Why do we tell it to process these requests using the isapi.dll instead of some saleslogix dll? Because the isapi.dll will look in the web.config for the application on which the request was made for an httphandler. We have one entry in our sData web.config for this purpose:

Why disable Integrated Authentication?

sDataandPO_IISSettings2
Now we are removing the Integrated Windows Authentication. The reason for this is that sData is not likely to have someone browsing to the website. Even if Lee is logged into the machine, it is unlikely that it is actually Lee going to the sData portal for his data. It is probably an application working on the request of Lee.

So like IE performs for us, we want Lee to be asked for his credentials so that we can filter the sData results by Lee’s security settings. So remove that setting and Lee will be asked for his credentials and these will be authenticated against the SalesLogix database. But wait! Our SalesLogix website (sdata portal) runs under a “webdll” user. This user has no permissions on the SalesLogix db through the provider to access Lee’s user record and verify his username and password. So how do we get the website into the database to see if what Lee enters at the username and password prompt is correct?

sDataandPO_adminsettings

Go into the Administrator

This is why we have to go into the administrator and change the settings there. We map a user – usually admin – to our “webdll” user. This says that when webdll, acting on behalf of the sdata portal (as setup in IIS under the identity tab) requests to check Lee’s credentials it will be admin who actually logs into SalesLogix and checks to see if Lee entered them correctly.

So we covered the three pieces.

  1. IIS, Config button
  2. IIS, Security Tab
  3. SLX Administrator

And we should now know why we do what we do in each.

But what could possibly go wrong?

Perhaps you forgot to set the appropriate settings in the administrator you will see db_sec_auth_failed or something similar:
sDataandPO_adminnotsettowebdll

Perhaps you forgot to map the dll? You will just see page cannot be found.

If you do not see the “SalesLogix Client” as in the following screen shot then you have likely missed a step in IIS:
sDataandPO_askingmeforcredsOK

I know we mainly referred to sData in this post, but the same settings apply to Process Orchestration or the Process Host portal. Remember the sData spec and more information can be found here: http://interop.sage.com

This setup stuff generally is an admin task, but developers need to be familiar with it as well.
So be sure to check out the videos on the topic in both the Administrators Subscription and Developers Subscription here:
http://sageu.com/saleslogix/subscriptions/. You can just search for “sData” or “Process” and will find several videos on the topic.

Tags: , , ,

sData in 7.5.2 (sData 1.0 Spec) and sData Client Libraries

Posted by Jason Huber on September 03, 2009
Developer / 2 Comments

There is a lot of talk right now about sData in 7.5.2 and the new client libraries that are being released. These libraries are designed to make accessing sData from SalesLogix easier, well really any sData source easier, but we are talking SalesLogix here. The idea is that the libraries will abstract some of the more common tasks that right now require you to write a lot of code and more importantly understand reading and writing xml. Why xml? because it is extensible of course, but really because sData is just an ATOM feed and ATOM is in xml format.
The important things to remember:

  1. 7.5.1 uses sData 0.9
  2. 7.5.2 use sData 1.0
  3. These client Libraries from the Gobi team are used for 7.5.2 and sData 1.0

You can find out more about sData at: http://interop.sage.com/

So how can we use these new sData client libraries? Well at the BootCamp in 2009 we did just that:

First we need the libraries (these are available as part of the 7.5.2 beta, so just ask).
Add a reference:
sDataclientLibrariesInReferences

Then I added some using statements:

using Sage.SData.Client.Atom;
using Sage.SData.Client.Common;
using Sage.SData.Client.Core;
using Sage.SData.Client.Extensions;
using System.Xml;

Then we need a couple form level variables (or at least this is how I did it):

//I put this up here because I need it in two methods.
// create the SDataService with url, user name, and password
SDataService _sdataService = new SDataService(@"http://localhost:3333/sdata/slx/dynamic/-/", "Lee", "");
SDataSingleResourceRequest _SingleResource;

Notice in the above code the new /-/ – that is an important change with sData. There is now a Payload in the feed. This is a breaking change for your 7.5.1 sData code.

So we have the dataservice created, now we need to actually initialize it:
// initialize it
_sdataService.Initialize();

Now that we have it initialized we can do many things, the two I have worked with thus far are:

SDataResourceCollectionRequest
//and
SDataSingleResourceRequest

Which really seem to correlate to query and read here:
http://interop.sage.com/daisy/sdata/Introduction.html

Anyway, if we want to get all accounts back:

SDataResourceCollectionRequest myrequest = new SDataResourceCollectionRequest(_sdataService);
myrequest.ResourceKind = "Accounts";
AtomFeed myFeed = myrequest.Read();

What do these look like at this point? An atom feed. You can hover over myFeed and read it in text or xml format and see it for yourself. What we want to do is load it into some sort of control. To do that we need to get it into a bindable object like a datatable:

DataTable myTable = new DataTable();
myTable.Columns.Add("Uri");
myTable.Columns.Add("AccountName");

foreach (var atomentry in myFeed.Entries)
{
var dr = myTable.NewRow();
dr[0] = atomentry.Id.Uri.AbsoluteUri;
dr[1] = atomentry.Title.Content;
myTable.Rows.Add(dr);
}

dataGridView1.DataSource = myTable;

Great! Now you can see it on the screen (assuming you have a gridview called dataGridView1).

The next thing you want to do is click on an Account and return the details for that account.
That is where we want the:

_SingleResource = new SDataSingleResourceRequest(_sdataService);

so we initialize the sDataSingleResourceRequest with the sdataservice we created earlier and now we can tell it which account to go get and that we expect an account back.


_SingleResource.ResourceSelector = sId;
_SingleResource.ResourceKind = "Accounts";

var entry = _SingleResource.Read();
_SingleResource.Entry = entry;

From here the entry is xml and you can change it as you see fit.

This entire video and code sample was part of SalesLogix Bootcamp ’09 and the video is in the developers subscription.

What comes next? We will look at actually updating the records. The code was at bootcamp and you can check out the resources there. I am told the client libraries will be made to actually know that you are returning an “Account” entity back and be able to handle it a bit better in the next version. In this version we save about 50% of our time, so big steps!

Tags: , , , ,