Integration with Adobe Marketing Cloud ( aka Neolane )

Crm landscapes are often made of disparate systems interacting , for example you can have ,apart from the crm itself, marketing segmentation tools, data warehouses, e-Commerce sites, online presence sites,etc.. In this scenario one of the leading marketing cloud solutions is Adobe with the recent acquisition of Neolane. At very basics (Neolane experts please forgive me!) Neolane it’s a tool to segment your customers (young & low salary, old & high spending, single women with kids,etc….), create personalized campaigns for them and delivering to them commercial offers or discounts etc… With Neolane you can track the results of these campaigns (for example the clicked links on the campaign email) and optimize your next campaigns.

From an integration perspective the classic way of import/export data with Neolane it’s the usage of files and workflows, but Neolane offers also other means to extract or update information on the system via API .

The Neolane API consists of a single endpoint usually listening here :

https://neolaneserver/nl/jsp/soaprouter.jsp

This service accepts only POST requests and using standard SOAP message exchanges can help you to extract or change the data that you want on Neolane.

However in order to access this resource you have to authenticate against Neolane. Once authenticated Neolane will give us a session token and a security token and using both we can finally query Neolane.

Theoretically you can also query the soap service passing user/password combination but this security option is by default disabled at configuration level.

For our purposes we will limit our analysis to these two soap services :

1) session (Logon method)

2) query (execute query method)

Now in order to proceed to the API calls we can proceed in two ways:

A) call this page https://neolaneserver/nl/jsp/schemawsdl.jsp?schema= for each service and passing the service name as parameter obtain the wsdl (you have to authenticate so go with your browser and save the wsdl to disk). For session you will make the call with this parameter xtk:session  , for execute query with this parameter xtk:queryDef. Once you have the wsdl you can use the tool of your choice to generate the classes for the integration (for .net wsdl.exe).

B) instead of having proxy classes that handle for you the burden of soap communication you can call directly https://neolaneserver/nl/jsp/soaprouter.jsp and modify the request headers according to the Soap Action (service) that you want to call.

We will go with option B for two reasons:

1) the authentication service (session) requires credentials passed inside the http header of the request and do this with .net it’s pretty cumbersome and involves the modification of the proxy classes (you will lose this if you generate again them against the wsdl)

2) the proxy classes generated , in my case at least, are not really that good … just to give you an example :
This the wsdl section for the Logon method of Session Service

 <s:element name="Logon">
  <s:complexType>
    <s:sequence>
       <s:element maxOccurs="1" minOccurs="1" name="sessiontoken" type="s:string"/>
       <s:element maxOccurs="1" minOccurs="1" name="strLogin" type="s:string"/>
       <s:element maxOccurs="1" minOccurs="1" name="strPassword" type="s:string"/>
       <s:element maxOccurs="1" minOccurs="1" name="elemParameters" type="tns:Element"/>
    </s:sequence>
  </s:complexType>
 </s:element>
  <s:element name="LogonResponse">
   <s:complexType>
     <s:sequence>
      <s:element maxOccurs="1" minOccurs="1" name="pstrSessionToken" type="s:string"/>
      <s:element maxOccurs="1" minOccurs="1" name="pSessionInfo" type="tns:Element"/>
      <s:element maxOccurs="1" minOccurs="1" name="pstrSecurityToken" type="s:string"/>
     </s:sequence>
   </s:complexType>
</s:element>

and this is the generated method

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("xtk:session#Logon", RequestNamespace="urn:xtk:session", ResponseNamespace="urn:xtk:session", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    [return: System.Xml.Serialization.XmlElementAttribute("pstrSessionToken")]
public string Logon(string sessiontoken, string strLogin, string strPassword, System.Xml.XmlElement elemParameters, out System.Xml.XmlElement pSessionInfo, out string pstrSecurityToken) {
        object[] results = this.Invoke("Logon", new object[] {
                    sessiontoken,
                    strLogin,
                    strPassword,
                    elemParameters});
        pSessionInfo = ((System.Xml.XmlElement)(results[1]));
        pstrSecurityToken = ((string)(results[2]));
        return ((string)(results[0]));
    }

so basically it requires you to pass parameters by reference (out keyword) while in reality there is no need for it.

So let’s go with classic HttpWebRequest:

 //Create the web request to the soaprouter page
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://neolaneserver/nl/jsp/soaprouter.jsp");

req.Method = "POST";
req.ContentType = "text/xml; charset=utf-8";
//Add to the headers the requested Service (session) that we want to call
req.Headers.Add("SOAPAction", "xtk:session#Logon");
//Here for testing purpouses username and password are here but you should acquire it in a secure way!
string userName = "username";
string pass = "password";
//We craft the soap envelope creating a session Logon reequest
string body = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:xtk:session\">" +
                "<soapenv:Header/><soapenv:Body><urn:Logon>" +
                "<urn:sessiontoken/>" +
                "<urn:strLogin>" + userName + "</urn:strLogin>" +
                "<urn:strPassword>" + pass + "</urn:strPassword>" +
                "<urn:elemParameters/>" +
            "</urn:Logon></soapenv:Body></soapenv:Envelope>";

//We write the body to a byteArray to be passed with the Request Stream
byte[] byteArray = Encoding.UTF8.GetBytes(body);

// Set the ContentLength property of the WebRequest.
req.ContentLength = byteArray.Length;
// Get the request stream.
Stream dataStreamInput = req.GetRequestStream();
// Write the data to the request stream.
dataStreamInput.Write(byteArray, 0, byteArray.Length);
// Close the Stream object.
dataStreamInput.Close();

var response = req.GetResponse();

Stream dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content on the console.
Console.Write(responseFromServer);
// Clean up the streams and the response.
reader.Close();
response.Close();

//Manually parsing the response with an XMLDoc
System.Xml.XmlDocument xResponse = new XmlDocument();
xResponse.LoadXml(responseFromServer);
// We parse manually the response. This is again for testing purpouses
XmlNode respx = xResponse.DocumentElement.FirstChild.FirstChild;

string sessionToken = respx.FirstChild.InnerText;
string securityToken = respx.LastChild.InnerText;
// We have done the login now we can actually do a query on Neolane
HttpWebRequest reqData = (HttpWebRequest)WebRequest.Create("https://neolaneserver/nl/jsp/soaprouter.jsp");
reqData.ContentType = "text/xml; charset=utf-8";
//Add to the headers the requested Service (ExecuteQuery) that we want to call
reqData.Headers.Add("SOAPAction", "xtk:queryDef#ExecuteQuery");
//Add to the headers the security and session token
reqData.Headers.Add("X-Security-Token", securityToken);
reqData.Headers.Add("cookie", "__sessiontoken=" + sessionToken);
reqData.Method = "POST";
// We write a SQL Like query to Neolane using XML syntax. Basically we are asking: SELECT email,firstname,lastname from recipient where email='test@test.com'
string bodyData = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:xtk:queryDef\">" +
                "<soapenv:Header/><soapenv:Body><urn:ExecuteQuery><urn:sessiontoken/><urn:entity>" +
                "<queryDef operation=\"select\" schema=\"nms:recipient\">" +
                    "<select><node expr=\"@email\"/><node expr=\"@lastName\"/><node expr=\"@firstName\"/></select>" +
                    "<where><condition expr=\"@email = 'test@test.com'\"/></where>" +
                "</queryDef>" +
            "</urn:entity></urn:ExecuteQuery></soapenv:Body></soapenv:Envelope>";

byte[] byteArrayData = Encoding.UTF8.GetBytes(bodyData);

// Set the ContentLength property of the WebRequest.
reqData.ContentLength = byteArrayData.Length;
// Get the request stream.
Stream dataStreamInputData = reqData.GetRequestStream();
// Write the data to the request stream.
dataStreamInputData.Write(byteArrayData, 0, byteArrayData.Length);
// Close the Stream object.
dataStreamInputData.Close();

var responseData = reqData.GetResponse();

Stream dataStreamData = responseData.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader readerData = new StreamReader(dataStreamData);
// Read the content.
string responseFromServerData = readerData.ReadToEnd();
// Display the content. Here we will see the query results in form of recipient collection
Console.Write(responseFromServerData);
// Clean up the streams and the response.
readerData.Close();
responseData.Close();

Using this technique you can simply pull or push data to Neolane without having to touch the Neolane system itself.

UPDATE : here you can see how to write to Neolane!

3 pensieri su “Integration with Adobe Marketing Cloud ( aka Neolane )

  1. kuko ha detto:

    Hi there,
    do you have some example how to create a new mailing or template? I can log in but nut much luck with creating the soap message for new mailing :/ and the docs really sucks…
    thanks for your effort

    "Mi piace"

Lascia un commento