Séance de sport REST API Salesforce et Biztalk

… ou comment générer ses schémas de type pivot via API REST SalesForce en un seul click

Si vous avez à mettre en place des flux BizTalk qui consomment l’API REST SalesForce, et que les objets de SalesForce vont être à peu de choses près vos schémas pivots, il est intéressant d’automatiser cette partie.
Pour que cela ne nous paraisse pas la côte finale de Paris-Versailles (souvent qualifiée d’interminable ^^), je vous propose une séance sportive intense !

BALANCE MAN… CADENCE MAN… TRACE LA GLACE C’EST LE COOOOOLLLL RASTA !!!!!!!!

rasta_rocket_depart

CIRCUIT

Dans notre cas, le but est d’appeler via un programme C# la méthode Describe de l‘API REST SalesForce et de transformer la réponse XML en schéma pivot XSD qui nous convient. Pour notre circuit, on prendra l’objet Account.

ECHAUFFEMENT

Pour utiliser l’API REST SalesForce, il faut avoir une application connectée dans SalesForce et obtenir un token de connexion en appelant la méthode OAuth 2.0. Ces étapes sont très bien expliquées (avec le code fourni) par Richard Serroter dans cet article :
https://developer.salesforce.com/page/Calling_the_Force.com_REST_API_from_BizTalk_Server

En utilisant le code du paragraphe « Build an WCF Behavior for Authentication », la première étape sera de créer les classes et la méthode pour obtenir le token.
Dans notre parcours, le projet prend cette forme :

Arborescence_projet

Astuce : Le nom de l’objet SalesForce choisi, version de SalesForce et nom du schéma XSD de sortie seront paramétrables, ainsi la génération de chaque nouveau schéma nécessitera uniquement changement d’un paramètre.

Le programme du circuit est de cette forme :

Projet_Get_Token

L’objet obtenu SfdcToken contient deux paramètres : le SessionId et la nouvelle URL de connexion pour appeler la méthode DESCRIBE.
Une fois le fameux token obtenu, les choses sérieuses commencent 🙂

TOUR DE PISTE 1 – Accélération

Maintenant, le but est de passer un appel GET à l’API REST SalesForce afin d’obtenir la description de l’objet Account. Dans notre cas, ce sera notre futur squelette du schéma pivot.
Pour cela, on créée une méthode GetSObjectDescribe avec une requête HttpClient qui va appeler la méthode REST GET de l’API avec l’URL /services/data/v36.0/sobjects/Account/describe. Le résultat de la requête est sauvegardé dans un XML.
Les méthodes sont de cette forme là :

        private async Task<XmlDocument> GetSObjectDescribe(string objectToCreate)
        {
            string restQuery = string.Format("/services/data/{0}/sobjects/{1}/describe", version, objectToCreate);
            var result = await GetSalesForceResource(restQuery);
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(result);
            return doc;
        }

        private async Task<string> GetSalesForceResource(string restQuery)
        {
            HttpClient queryClient = new HttpClient();

            var tokenSf = SfdcTokenManager.GetSession(
                               EndPointConfiguration.OAuthConsumerKey,
                                 EndPointConfiguration.OAuthConsumerSecret,
                                EndPointConfiguration.SfdcUsername,
                                 EndPointConfiguration.SfdcPassword,
                                 string.Empty, EndPointConfiguration.LoginUrl);

            restQuery = string.Format("{0}{1}", tokenSf.ServiceUri, restQuery);
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, restQuery);

            request.Headers.Add("Authorization", "Bearer " + tokenSf.AuthorizationToken);

            request.Headers.Add("Accept", "application/xml");
            HttpResponseMessage response = await queryClient.SendAsync(request);
            var result = await response.Content.ReadAsStringAsync();
            return result;
        } 

Le XML renvoyé est suivant :

Projet_Get_Describe_XML

TOUR DE PISTE 2 – Accélération toujours !

rasta_rocket_encours

Tenez bon !
Une fois la description de l’objet au sens SalesForce obtenue, l’effort est de transformer ce XML en XSD pivot via un fichier XSLT.
Dans notre cas, le schéma à obtenir est le suivant :

Schema_Account_Cible

Ainsi, on pourra envoyer des messages à SalesForce de type PATCH (insertion/mise à jour des accounts) et de type TREE (insertion de plusieurs lignes à la fois).
Pour ce faire, dans notre programme C#, on créé un document XML avec le nœud parent <Accounts> , puis on insère le XML obtenu précédemment dans ce nœud.
La suite est d’appliquer un XSLT pour transformer ce document dans un document XSD de type Pivot.
J’ai utilisé ce code là pour la transformation :

 
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
                xmlns:tns="urn:enterprise.soap.sforce.com"
                 xmlns:urn="urn:enterprise.soap.sforce.com">
  <xsl:output method="xml" indent="yes"/>

  <xsl:param name="generateRelation"/>
  <xsl:param name="targetNameSpaceParam"/>

  <xsl:template match="/" >
    <xs:schema targetNamespace="{$targetNameSpaceParam}"
           xmlns:xsd="http://www.w3.org/2001/XMLSchema"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:tns="urn:enterprise.soap.sforce.com"
           xmlns:urn="urn:enterprise.soap.sforce.com">
      <xs:import namespace="urn:enterprise.soap.sforce.com" schemaLocation=".\XMLSalesForceSchemas.xsd" />
      <xs:annotation>
        <xs:appinfo>
          <references xmlns="http://schemas.microsoft.com/BizTalk/2003">
            <reference targetNamespace="urn:enterprise.soap.sforce.com" />
          </references>
        </xs:appinfo>
      </xs:annotation>
      <xsl:apply-templates select="/SObjects/*" />
    </xs:schema>
  </xsl:template>

  <xsl:template match="/SObjects/*" >
    <xsl:variable name="varLabelPlural" select="local-name(.)"/>
    <xs:element name="{$varLabelPlural}" xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:complexType>
        <xs:sequence>      
          <xs:element ref="s0:records" xmlns:s0="TextToReplace"/>
        </xs:sequence>
      </xs:complexType>
    </xs:element>
    <xs:element name="records" xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:complexType>
        <xs:sequence>
          <xsl:apply-templates select="./fields" />
        </xs:sequence>
        <xsl:variable name="varName" select="./name"/>
        <xs:attribute name="type" type="xs:string" use="required"/>
        <xs:attribute name="referenceId" type="xs:string" use="required" />
      </xs:complexType>
    </xs:element>
  </xsl:template>

  <xsl:template match="/*/*/fields">
    <xs:element name="{name}" type="{soapType}" minOccurs="0" >
      <xsl:attribute name="nillable">
        <xsl:value-of select="nillable" />
      </xsl:attribute>
    </xs:element>
  </xsl:template>

</xsl:stylesheet>

Et ce code là pour la création du XSD :

        public void transformToXSD()
        {

            var output = new XmlDocument();
            using (Stream strm = Assembly.GetExecutingAssembly().GetManifestResourceStream("CO.EAI.SalesForce.salesforce_V1.xsl"))
            using (MemoryStream ms = new MemoryStream())
            using (XmlReader reader = XmlReader.Create(strm))
            {
                XslCompiledTransform xslt = new XslCompiledTransform();
                xslt.Load(reader);

                XsltArgumentList list = new XsltArgumentList();
                xslt.Transform(_globalSchemaDescribeObject, list, ms);

                ms.Position = 0;
                output.Load(ms);
            }

            _globalSchemaDescribeObject.LoadXml(output.InnerXml);

        }

        public void save(string path)
        {
            _globalSchemaDescribeObject.Save(path);
            var directory = Path.GetDirectoryName(Path.GetFullPath(path));
            using (Stream strm = Assembly.GetExecutingAssembly().GetManifestResourceStream("CO.EAI.SalesForce.XMLSalesForceSchemas.xsd"))
            using (FileStream fstrm = File.Create(Path.Combine(directory, "XMLSalesForceSchemas.xsd")))
            {
                strm.Seek(0, SeekOrigin.Begin);
                strm.CopyTo(fstrm);
            }
        }

ARRIVEE

Et voilà, l’entrainement est terminé pour aujourd’hui !! On peut maintenant se reposer, en ayant en un seul click la génération des schémas de forme voulue (type pivot ici) pour n’importe quel objet de SalesForce.

rasta_rocket_arrivee

En espérant que cette séance vous ait plu,
Sportivement votre 🙂

Pas de commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *