Skip navigation

This is the 2nd step in the series : Bulk API How To - Import Custom Objects. In this step we'll define the structure or template used for the import.

The import structure contains the following properties :

  • name : string
  • fields : list (obtained in step 1 - setup your import)
  • update rule : { always, ifNewIsNotNull, ifExistingIsNull, useFieldsRule }
  • identifierFieldName : string (the unique identifier for the record)
  • secondsToRetainData : int
  • isSyncTriggeredOnImport : bool
  • syncActions : { add, remove, subscribe, unsubscribe, markActive, markComplete }

 

The sample project provides a method to create the import structure :


public Import CreateImportStructure(intid,Dictionary<string,string>fields)
        {
            Import import = new Import()
                                {
                                    name = "sample import",
                                    fields = fields,
                                    updateRule = RuleType.ifNewIsNotNull,
                                    identifierFieldName = "Email_Address1",
                                    secondsToRetainData = 3600,
                                    isSyncTriggeredOnImport = true,
                                };

            RestRequest request = new RestRequest(Method.POST)
                                      {
                                          RequestFormat = DataFormat.Json,
                                          Resource = string.Format("/customObject/{0}/import", id)
                                      };
            request.AddBody(import);

            IRestResponse<Import> response = _client.Execute<Import>(request);
            return response.Data;
        }




 

The fields collection can be obtained from the first step : Setting up your Import.

The sample code below shows a typical fields collection :

 

            // define the list of fields used in the import
            // for the purposes of this sample, the fields have been hardcoded
            Dictionary<string, string> importFields = new Dictionary<string, string>
                                                          {
                                                              {"Email_Address1", "{{CustomObject[123].Field[456]}}"},
                                                              {"First_Name1", "{{CustomObject[123].Field[456}}}"},
                                                              {"Last_Name1", "{{CustomObject[123].Field[456]}}"},
                                                              {
                                                                  "EmailAddressField",
                                                                  "{{CustomObject[123].Contact.Field(C_EmailAddress)}}"
                                                                  }
                                                          };

            // create the structure for the import
            Import import = _helper.CreateImportStructure(customObjectId, importFields);




 

The complete source code for this sample is available here on Github.

Please note that I've written the code to try and help and I'll do my best to support it, but it is not supported by Eloqua.

 

Thanks,
Fred

fsakr

Importing Custom Objects - Setup

Posted by fsakr Sep 29, 2012

In this post, we'll describe the steps necessary to setup your custom object import. Before starting the import, we'll need to know a few things about :

  • custom objects : to identify where the data is going
  • custom object fields : to identify the data

 

The sample project available on Github contains two methods to help with this.

The models are available here.

 

Search for Custom Objects

Eloqua gives users access to customize their data, as such the definition of a custom object will be different for each client.

Let's look at a simple method to search for custom object records.

 

        /// <summary>
        /// Returns a list of <see cref="CustomObjectSearchResponse"/>
        /// </summary>
        /// <param name="searchTerm">The name of the custom data object to search for</param>
        /// <param name="page">The page number (see response total)</param>
        /// <param name="pageSize">The number of records to include in each page of results</param>
        public RestObjectList<CustomObjectSearchResponse> SearchCustomDataObjects(string searchTerm, int page, int pageSize)
        {
            var request = new RestSharp.RestRequest(Method.GET)
                              {
                                  Resource =
                                      string.Format("/customObjects?search={0}&page={1}&pageSize={2}", searchTerm,
                                                    page, pageSize)
                              };
            IRestResponse<RestObjectList<CustomObjectSearchResponse>> response = _client.Execute<RestObjectList<CustomObjectSearchResponse>>(request);
            return response.Data;
}




Describe Custom Object

The import will need to know about the custom object's fields.

The following method provides a list of fields for a custom object record.


 /// <summary>
        /// Retrieve a <see cref="CustomObject"/> by its uri
        /// </summary>
        /// <param name="id">The uri of the <see cref="CustomObject"/></param>
        /// <returns></returns>
        public RestObjectList<Field> GetCustomObjectFields(int id)
        {
            var request = new RestRequest(Method.GET)
                              {
                                  Resource = string.Format("/customObject/{0}/fields?", id)
                              };
            IRestResponse<RestObjectList<Field>> response = _client.Execute<RestObjectList<Field>>(request);
            return response.Data;
        }




The complete source code for this sample is available here on Github.

Please note that I've written the code to try and help and I'll do my best to support it, but it is not supported by Eloqua.

 

Thanks,
Fred


The Bulk API allows users to import and export large volumes of data into Eloqua. In a previous post. we described how to Upload Contacts into a List using the API.

In today's post, we'll demonstrate how to Import Custom Objects (Data Cards) using the Bulk API.

 

There are 4 main steps involved in a Bulk Import

  1. Setup your Import
    - identify fields : where the data goes
  2. Create the Definition or Structure for your Import
  3. Upload the Data
  4. Check the Status of your Import

 

A complete working sample of the code is available here on Github.

The project contains all of the models and methods,  as well as a test project to demonstrate the process.

 

The Test project serves to document as well as test the operations provided. Let's look at a complete test of a Custom Object Import :

 

Let's start by searching for and identifying all of the custom objects in your install :

 

// search for custom objects
RestObjectList<CustomObjectSearchResponse> search = _dataHelper.SearchCustomDataObjects("*", 1, 50);
List<CustomObjectSearchResponse> customObjects = search.elements;





We'll assume that you've identified a single Custom Object from the list :

int customObjectId= 1;





Eloqua allows clients to customize their Custom Objects. We'll need to identify the fields for this custom object.

From the list above, we'll select the list of fields that will be used as part of the import.


Dictionary<string,string>importFields=newDictionary<string,string>
                                                          {
                                                              {"Email_Address1", "{{CustomObject[48].Field[370]}}"},
                                                              {"First_Name1", "{{CustomObject[48].Field[371]}}"},
                                                              {"Last_Name1", "{{CustomObject[48].Field[372]}}"},
                                                              {
                                                                  "EmailAddressField",
                                                                  "{{CustomObject[48].Contact.Field(C_EmailAddress)}}"
                                                                  }
                                                          };






We can now create the definition (structure) for the import.


Import import = _helper.CreateImportStructure(customObjectId,importFields);
string importUri = import.uri;





The next step is to define some data for the import :


Dictionary<string,string> data = new Dictionary<string,string>
                                                  {
                                                      {"Email_Address1", "sample"},
                                                      {"First_Name1", "sample"},
                                                      {"Last_Name1", "sample"},
                                                      {"EmailAddressField", "sample@test.com"}
                                                  };





List<Dictionary<string,string>> list = new List<Dictionary<string,string>>
{
data,
};




 

Finally, we'll import the data and verify the results :


Sync sync = _helper.ImportData(importUri , list);
RestObjectList<SyncResult> syncResult = _helper.CheckSyncResult(sync.uri);




 

Please note that the code samples referenced here are my own work and something that I've put together to try and help.

I'll do my best to support the code, but please understand that this code is not supported by Eloqua.

 

Thanks,

Fred

Now that we've seen:

Authentication

HTTP Verbs

URL Parameters

 

It's time to look at HTTP Request Headers.  The table below outlines the request headers used by the API.

 

Content-Type

Specifies the media type that the client is sending to the server. Use application/json.  If no value is supplied, an error will occur.

 

For example:

PUT https://.../data/contact/123

Content-Type: application/json

<the existing contact>


Note: For PUT and POST verbs, use of the Content-Type header is mandatory.

Accept

Specifies the media types that the client is willing to accept from the server.  If no value is supplied, or if the supplied value doesn't contain "application/json" then the response will be returned in JSON.

 

GET https://.../data/contact/123 Accept: application/json

GET https://.../data/contact/123

GET https://.../data/contact/123 Accept: text/html

 

Note: use of the Accept header is optional.

X-HTTP-Method-Override

Allows HTTP clients, that don't support the PUT or DELETE verbs, to use GET or POST to update and delete entities. If a verb value is supplied in the header, that value will be used in place of the actual request verb.

In the example below, both requests are functionally identical:

DELETE https://.../data/contact/123

GET https://.../data/contact/123

X-HTTP-Method-Override: DELETE

 

Note: use of the X-HTTP-Method-Override header is optional.

X-HTTP-Status-Code-Override

Allows HTTP clients that don't support or expose HTTP status codes, other than HTTP 200 OK, to indicate that the server should always respond with a specific HTTP status code.  If a status code value is supplied in the header, that status code will be used in place of the actual request status code.

In the example below, the request will always return HTTP 200 OK, regardless of whether or not the actual status is OK:

 

 

    GET https://.../data/contact/123

    X-HTTP-Status-Code-Override: 200

 

 

If a value is supplied for the header, the actual status code will be returned in the X-HTTP-Original-Status-Code response header.

Note: use of theX-HTTP-Status-Code-Override header is optional.

Now that we've seen how to Authenticate, and looked at HTTP Verbs, let's move on to the URL parameters supported by the API endpoints.


Most Eloqua API endpoints accept one or more standard parameters, but not all endpoints support ALL of these parameters.

 

The available parameters are outlined in the table below.

 

 

Id

Identifier for the entity on which to perform the operation  This value is actually part of the base URL, and not a URL parameter. An example can been seen in the request below, which would return a representation of the contact entity with the identifier 123:

GET https://.../data/contact/123

Note: The format of entity identifiers may change over time, so developers should treat entity identifiers as opaque strings and not as numbers.
DepthDepth or level of detail returned. This can be "minimal", "partial", or "complete". An example can been seen in the request below, which would return the root form folder with a depth of "complete":
GET https://.../assets/form/folder/root?depth=complete
CountMaximum number of entities to return. The value can be any whole number between 1 and 1000. An example can been seen in the request below, which would return a maximum of 20 landing pages:
GET https://.../assets/landingPages?count=20

Note: If the count parameter is not supplied, 1000 will be used by default.
Page

Specifies which page of entities to return, and can be any positive whole number.  The Count parameter determines the size (number of results) per page.  An example can been seen in the request below, which return the first page of 20 landing pages (results 1...20):


GET https://.../assets/landingPages?count=20


To return the second page of 20 landing pages (results 21...40):

 

GET https://.../assets/landingPages?page=2&count=20

 

To return the third page of 20 landing pages (results 41...60):

 

GET https://.../assets/landingPages?page=3&count=20


Note: If the page parameter is not supplied, 1 will be used by default.

Search

Specifies the search criteria used to retrieve entities. This is in the form of:

[{term} {operator}] {value}

 

Where {term} is the name of a field to filter on, {operator} is the comparison operator, and {value} is the value to compare the field with.

 

The following request would return all landing pages whose name contains the word "Test":

GET https://.../assets/landingPages?search=Test

 

Note: If {term} and {operator} are not supplied, the name field is compared to the value using the equality operator.

SortSpecifies the name of the property used to sort the returned entities. The value depends on the type of entity being sorted, with each entity having its own list of sortable properties. An example can been seen in the request below, which returns a list of contacts sorted by first name (Note: firstName and lastName are only relevant to contacts):

 

GET https://.../data/contacts?sort=firstName

 

...or by last name


GET https://.../data/contacts?sort=lastName

 

 

 

 

Note: If  the Sort parameter is not supplied, the the results are returned with a default sort.

Dir

Specifies the direction in which to sort the returned entities. The value can be "asc" for ascending or "desc" for descending.  So, for the sorting example by first name above:

GET https://.../data/contacts?sort=firstName&dir=asc

Note: If the dir parameter is not supplied, the default value "asc" is used.

StatusCodeOverride

Allows HTTP clients that don't support or expose HTTP status codes, other than HTTP 200 OK, to indicate that the server should always respond with a specific HTTP status code. If a value is supplied for the parameter, that value will be used as the status code. An example can been seen in the request below, which will always return HTTP 200 OK, regardless of whether or not the actual status is OK:

 

GET https://.../data/contact/123?statusCodeOverride=200

JsonpCallback

Indicates that the response should be returned as JSONP (or JSON with Padding). The value is used as the name of the callback function in the returned JavaScript. The value of the Accept request header is ignored, and the content returned is always of type application/javascript.

An example can been seen in the request below, which does not include the jsonpCallback parameter, and so the response is plain JSON:

GET https://.../system/user/current?depth=complete  HTTP/1.1 200 OK Content-Type: application/json  {"type":"User","createdAt":"1237228178","depth":"complete", ...}
...and the same request made with the jsonpCallback parameter.  (returns application/javascript, and the original JSON is now wrapped in a call to the specified callback function):
GET https://.../system/user/current?depth=complete &jsonpCallback=loadCurrentUser  HTTP/1.1 200 OK Content-Type: application/javascript  loadCurrentUser({"type":"User","id":"9","createdAt":"1237228178","depth":"complete",...});

The Eloqua API utilizes HTTP basic authentication. Each request must include an Authentication header, with a base-64 encoded value.

 

The header should be in the form of:

siteName + '\' + username + ':' + password

 

Here's what an example, using a site name of "COMPANYX", a username of "user1", and password of "password123", would look like:

COMPANYX\user1:password123

 

Now, base-64 encode the above string to get:

 

Q09NUEFOWVhcdXNlcjE6cGFzc3dvcmQxMjM=

 

We then use that string to create the Authentication Header:

 

Authorization: Basic Q09NUEFOWVhcdXNlcjE6cGFzc3dvcmQxMjM=

 

For further information on HTTP Basic Authentication, see: RFC 2617 - HTTP Authentication: Basic and Digest Access Authentication

 

Now that you've authenticated, it's time to make a Request.  Let's start with Eloqua REST API - HTTP Verbs

In a previous post, we looked at Testing your Cloud Component Provider using the Eloqua Cloud Component Test Utility.

 

Now that you've tested your Provider and all of it's Endpoints, you're ready to submit your components for approval and registration in Eloqua.

 

In order to have your components registered, so that they appear in Eloqua for use, you will need to go through the following approval process:


  1. Use the Test App to make sure that your component(s) are *fully* working.
  2. Click the Submit button in the test app, which will submit your component provider for approval.
  3. The approval team at Eloqua will receive an email informing them that your components are ready to be reviewed.
  4. If further information is required (i.e. usage instructions, test credentials), a member of the approval team will contact you.
  5. The approval team will test the component(s) and either approve or deny the submission.
  6. You will be informed of the decision.

 

If all goes well, your component(s) will be approved and passed on to the deployment team at Eloqua for registration within the application.

 

Some items to note:

 

  • Once registered in Eloqua, your component(s) will be available to all Eloqua instances (there is currently no way to have it registered in a single instance, but they will have to be enabled in order to be used see next item).
  • The component(s) will need to first be enabled by an Eloqua customer administrator to be used in a particular Eloqua instance.