jump to navigation

Running through the first finish line June 19, 2008

Posted by mmoriar1 in Uncategorized.
add a comment

Since last time, I’ve tentatively completed all of the Subscriber pages (yaaay) and can now start working on tracking events.  Here is a rough overview of this post’s content:

  • I changed the repeater on the single subscriber pages to be more dynamic based on the type of data being entered, so I will share my experiences with that
  • I got upsert working, and will give a few pointers on the syntax of that
  • I will explain what I have found out about deleting subscribers
  • I handled the results and statuses of the multiple subscriber actions by using a GridView in conjunction with a DataTable, I will go into more detail about that
  • A few more odds, ends, and annoyances

So.  The repeater on the single-subscriber pages is different than it was last time.  Firstly, there’s only one, not two.  I changed things around in that instead of having one repeater just for the text fields and the other repeater just for the attributes which have picklists, there’s now just one repeater which contains a text box, drop down list, and check box in each table row.  Doing this allows the repeater to select which control will be visible based on the datatype of the attribute or the presence of a picklist.  It also looks more professional and will be more flexible to different clients’ attribute pools.  Take a look:

OLD

Old

NEW:

New

As you can see, the new page looks more aligned and organized.  You might also notice the “upsert” checkbox.  Upsert does indeed work when that box is checked, but it’s hard to figure out how to implement it from the API guide alone (at least for me).  For those of you who don’t know what an “upsert” call does, it is basically creating the subscriber, but if the subscriber already exists then instead of throwing an error it will update the existing subscriber with the information from the create call.  To enable upserting on a create call, use the following code:

CreateOptions co = new CreateOptions();
co.SaveOptions = new SaveOption[1];
SaveOption so = new SaveOption();
so.PropertyName = “*”;
so.SaveAction = SaveAction.UpdateAdd;
co.SaveOptions[0] = so;

…and then pass the CreateOptions object to the call like so:

CreateResult[] results = integrationFramework.Create(co, new APIObject[] { new_sub }, out
requestID, out status);

Booya, there you go.  You’re now upserting.

Additionally, with regards to the single-subscriber pages, a workaround for the problem in the previous post of not being able to specify an EmailTypePreference upon creation is to simply perform an update call on the created subscriber making sure to specify the desired EmailTypePreference.  The update call correctly sets the preference.

Next, I experimented with the deletion of subscribers.  Deleting subscribers is a tricky subject.  This is probably because most clients would never want to delete a subscriber since then all data related to that subscriber is lost.  The more elegant and useful way would be to unsubscribe them from everything and then just let their email address slip away into oblivion.  What I’ve found is that deleting a subscriber doesn’t delete them right away.  If you try and re-create the deleted subscriber after the deletion, you’ll find that you cannot, and an error will indicate that the subscriber is still present on the all-subscriber list.  Hmm…interesting.  Even more interesting is that while you cannot retrieve or re-delete the “deleted” subscriber.  You can upsert/update using the deleted subscriber’s email address.  Upserting the deleted subscriber brings them back to life in the subscribers list.  I don’t know as of yet the full internals of a deletion through ExactTarget, but that is all that I have discovered so far.

Next I constructed the multiple-subscriber pages.  The construction of the pages was simple: the idea was to have a user upload a file of comma-separated subscribers to be retrieved/created/deleted (in the ExactTarget format with attribute headers) and then to operate on that file.  File upload was easy (consult http://www.asp.net/learn/videos/video-255.aspx) and one thing I did was persist the file location in the Session object so that users only had to upload the file once and could then do create/retrieve/delete using the same stored file without having to upload every time.

Multiple-Creates/Retrieves/Deletes are all performed using the same API call syntax as in the single-subscriber pages, just instead of passing the API call one Subscriber to operate on, an array of Subscribers or email addresses is passed.  The tricky part was controlling the timeouts of the requests.  As of now, no more than 200 subscribers can be operated on without the request timing out.  Technically the limit is 2500, but if you have that many subscribers to create, ftp the file over to ET and do it that way. 🙂

After the call is performed, the results are displayed to the user using a GridView bound to a DataTable.  A DataTable is a neato object which is basically a database accessed like a variable.  I consulted Google quite a bit when learning about them, so I suggest that you do too.  Once the API call pops back the array of output, I populate the DataTable with that data and then bind it to the GridView.  I then consulted these resources for enabling paging and sorting for the GridView (http://www.csharpfriends.com/Forums/ShowPost.aspx?PostID=36859 , http://forums.asp.net/t/956540.aspx?PageIndex=1), and these resources for hooking up the GridView with a DetailsView for a Master/Details layout (http://www.asp.net/learn/videos/video-08.aspx , http://www.asp.net/learn/videos/video-07.aspx).  See the result:

STATUS OF THE CALL IN A GRIDVIEW:

RETRIEVED SUBSCRIBERS IN A MASTER/DETAILS VIEW:

Some odds and ends I learned are as follows:

  • It took me a long time to debug the fact that my email addresses had whitespace and newlines (\n) on the end of them, so to fix this problem I used the handy .TrimEnd() function to get rid of all the whitespace following the string.
  • It’s very annoying of the API to not give good error messages.  Error code 2, for example, says “There was an unexpected error”.  Thanks!  What was the error?!  After debugging, it turns out the first attribute in my created subscriber’s attribute array was null, and ET didn’t like that very much.  Would it really be that hard to say “Attribute cannot be null”.  I’m done complaining, the product really is awesome. 🙂

That should be it for now unless I forgot to mention something.

Until next time,

Mike

Advertisements

I’ve got my helmet on June 11, 2008

Posted by mmoriar1 in Uncategorized.
add a comment

Man, they’ve got me running through walls over here. 🙂 Two issues have come up in the last week or so, both of which are unresolved and can be found here:

http://developers.exacttarget.com/WEB/forums/t/522.aspx
http://developers.exacttarget.com/WEB/forums/t/520.aspx

If those pages are inaccessible, the issues are:

  • When creating a subscriber using the WebServices API, setting their EmailTypePreference to HTML or Text will always result in the creation of a subscriber with a preference of HTML.
  • The Describe call does not grab custom preferences from a user’s account. The Retrieve call does, but if there is no previous subscriber to retrieve, then we cannot get custom preferences using the Web Services API.

Aside from that, I’ve made some significant progress on the Subscriber pages. This progress includes completion of basic functionality of the create, update, delete, and retrieve pages, and the completion of more advanced aesthetic functionality on the create page. To sum up this aesthetic functionality, I present the following:

  • The default values of each attribute are loaded into their proper fields
  • The required attributes are denoted as such by a ‘*’ next to their names
  • The hidden attributes aren’t displayed on the page
  • If an attribute has a picklist of items to choose from, then the items are loaded into a drop down list with the default item initially selected
  • Each field’s data requirements are shown to the user if there is no default value (for example, if the field was “Birthday” then the corresponding text box would be populated with “Enter a date (DD-MM-YYYY)”
  • The hidden source code panel was filled with a text area with some text in it just to see how it looked. Until I figure out how to style it, that’s how it will stay (the style seems to not want to work correctly).

The create page will be complete once the two issues are resolved and I implement checkboxes into the repeaters. Here you can see the create page:

My attention for today will be solely placed on making the update page look like the create page. The update page is functional, but it needs a little bit more TLC.

Two and a half major issues which cost me a bunch of time over the past few days included the following:

  • When populating a new (uncreated) subscriber’s attributes array, you have to instantiate each attribute.This won’t work (will throw exception):new_sub.Attributes[i].Name = name
    new_sub.Attributes[i].Value = value
    This is the fix:com.exacttarget.webservice.Attribute att = new com.exacttarget.webservice.Attribute();
    att.Name = name;
    att.Value = value;
    new_sub.Attributes[i] = att;
  • When trying to fill a drop down list control inside of a repeater, consult google 🙂 Here’s what resources I used:
    http://p2p.wrox.com/topic.asp?TOPIC_ID=7731
    http://p2p.wrox.com/topic.asp?TOPIC_ID=14940

    I found that the binding of the drop down list to the list of items that is to populate it has to be done in the repeater’s ItemDataBound event. Basically what I did was in the describe I found out which attributes had picklists and then created an arraylist of DictionaryEntries (key-value pairs) of these attributes. The key of each DictionaryEntry was the name of the attribute and the value was another arraylist filled with the picklist items. Then, in the repeater’s ItemDataBound event, I bound each drop down list created to an arraylist of picklist items.

    The ItemDataBound event was created as such:
    this.Repeater.DataSource = pick_list;
    this.Repeater.ItemDataBound += new RepeaterItemEventHandler(Repeater2_ItemDataBound);
    this.Repeater.DataBind();

    Then I created a function:
    private void Repeater2_ItemDataBound(object source, RepeaterItemEventArgs e)
    {
    if (e.Item.ItemType == ListItemType.AlternatingItem ||
    e.Item.ItemType == ListItemType.Item)
    {
    bind drop down list here…
    }
    }

    That took awhile to figure out.

  • The half issue which took awhile to debug was that in the update/retrieve calls, I was forgetting to set my SimpleFilterPart to the RetrieveRequest, which meant that it just kept retrieving ALL of the subscribers and displaying the first one that it got instead of filtering out all of the subscribers except for the one I was retrieving/updating.

So that’s pretty much it, if I think of anything else that came up I’ll post about it.

All for now,

Mike

Trials and tribulations of the ET WebServices API June 5, 2008

Posted by mmoriar1 in Uncategorized.
Tags: ,
7 comments

Since last time I have implemented a (seemingly working) login system, set up the divs for the first draft of the page layout, validated the email address field, made some magic happen with repeaters, created a cool AJAX collapsible panel, and set up the site map. Extremely long sentence aside, I’ve been pretty productive.

The login system was a tricky fellow to design, but implementation was fairly simple. While not elegant, it IS functional. The tricky parts were figuring out how to authenticate the username/password with the ExactTarget system, how to go about storing the username/password for use in subsequent API calls, and how to prevent unauthenticated users from accessing other parts of the site. These difficulties were overcome by:

  • Performing a dummy API call to the ET servers and seeing if any exceptions are thrown. If an exception was thrown by the authentication, that message is displayed neatly to the user under the two username/password text boxes. If no exception was thrown, then the user is redirected to a start page.
  • Storing the username/password in the Session object. Session is naturally encrypted and is persisted as the user travels from page to page, so it works quite well.
  • Storing a boolean variable in the Session pertaining to whether or not the user has authenticated with the ET servers or not. At the top of each “restricted” page (in the page load), the page checks to see whether the user is authenticated, and if not it redirects to the login page. Despite a few display quirks, it works well.

Setting up the divs for the page layout was nothing more than fiddling with CSS. Here is what the page looks like as of now (draft 1). Ignore the debugging background colors 🙂

Validating the email field was quite easy as well after watching a tutorial video on the subject. (http://asp.net/learn/videos/video-193.aspx) It just makes sure that something is in the box using the RequiredFieldValidator and makes sure that it follows the name@site.domain format using the RegularExpressionValidator.

The magic with repeaters was fun. The idea was to fetch a Subscriber’s attributes (First Name, Last Name, Email, etc.) on page load and then display the attributes in editable text boxes so that a user could change their value(s), and then the Subscriber could be updated with an API call to the ET server. After debugging a few frustrating errors in the API/C# (discussed below), I loaded the Subscriber’s attributes into an ArrayList which was then bound to a repeater (guide consulted: http://stanleycn.blogspot.com/2006/10/how-to-bind-arraylist-to-repeater.html). The repeater simply displayed each attribute’s name on a label followed by a text box pre-populated with the attribute’s value. The result can be seen in the above image. One other thing that had to be done was to cast the Container.DataItem object in the repeater template to an attribute object like so: ((com.exacttarget.webservice.Attribute)Container.DataItem).

The AJAX in the page so far is little more than an UpdateProgress control used to visually indicate when the client is requesting stuff from the server (resources used: http://asp.net/learn/ajax-videos/video-123.aspx for the guide and http://ajaxload.info/ for the awesome animated gif) and a CollapsiblePanelExtender in order to hide/show the source code for the API call (guide: http://asp.net/learn/ajax-videos/video-89.aspx)

One time consuming debug that I had to do stemmed from the fact that in the API Guide example code on page 40 there is the line:

PartnerAPI.Send send = results[cntr] as Send;

which I reformed to say:

PartnerAPI.Subscriber sub = results[cntr] as Subscriber;

Seems OK, right? All of the rest of my code was correct, but I kept getting the compilation error that “it could not convert com.exacttarget.webservice.APIObject to Subscriber via an internal conversion”. But it had worked the previous day in its current state…hmmm… Well, after trying stuff, it turns out that it needed to be declared as such:

com.exacttarget.webservice.Subscriber sub = results[cntr] as com.exacttarget.webservice.Subscriber

Yep, fully qualified and all. Still haven’t figured out totally why that is, but it works and is pretty low on my priority list to clean it up.

The other time consuming bug which hit me was that when trying to grab a subscriber’s attributes, I would include “Attributes” in the properties list as such:

sub_request.Properties = new string[] { “ID”, “EmailAddress”, “Attributes” };

This makes sense, as Attributes are a member of the Subscriber object and are returned as an array of the Attribute object. But upon performing the retrieve call, I would get an error indicating that “the format doesn’t match the form’s field view” (or something similar). This is because you aren’t supposed to include “Attributes” in the properties list, as they are grabbed by default. I stumbled upon this fix in the developers.exacttarget.com forum. Problem solved.

Well that’s all for now, until next time,

Mike