Beispiel Anbindung SalesViewer API mit dem SDK-Konnektor

Die SalesViewer-API (Dokumentation: https://salesviewer.github.io/salesviewer-api/definition) liefert je Aufruf geschachtelte Daten:
Session ⟶ enthält Visits (Liste) und Company (Objekt).
Die Standard-REST-Verbindung kann diese Teilobjekte nicht separat als eigenständige Objekte ausgeben.

Ziel: Session, Visit und Company in eigene Schemaobjekte auftrennen, um sie mit Syncler einzeln abrufen, transformieren und synchronisieren zu können.

Lösung: Einsatz des SDK-Konnektors mit zwei C#-Skripten

  1. Schema-Skript – beschreibt die vier Schemaobjekte
  2. GetData-Skript – ruft SalesViewer ab, entfaltet die verschachtelten Daten und gibt die gefragte Objektmenge zurück

Voraussetzungen

  • Ein SDK-Konnektor in Syncler (mit API-Key als Parameter, z. B. ApiKey)
  • Zwei Skripts im SDK-Konnektor:
    • SalesViewerSchema (liefert Schema/Metadaten)
    • SalesViewerGetData (liefert Daten)
  • Optional: Sync-Konfiguration je Objekt (company, visit, session, sessionCompany) inkl. Änderungsgrenzwert (Feld lastActivityAt)

1) Schema-Skript (C#)

  • Definiert vier Schemaobjekte:
    • company – eindeutige ID: id, Updated-Feld: lastActivityAt
    • visit – eindeutige ID: id, Updated-Feld: lastActivityAt
    • session – eindeutige ID: guid, Updated-Feld: lastActivityAt; enthält kopiertes Feld companyID
    • sessionCompany – Variante des Session-Schemas, eindeutige ID: companyID (Session auf Company verdichtet)
  • Liefert die Schemaobjekte als JSON-Array
using SIS;
using Newtonsoft.Json.Linq;

public class SalesViewerSchema
{
    // SDK helper
    public SisHelper Helper { get; set; }

    // Your operation to return json data
    public string Execute()
    {
        #region Company Schema

        JObject Company = new()
        {
            { "title", "company" },
            { "uniqueidentifier", new JArray() { "id" } }
        };

        //Company Eigenschaften
        JObject CompanyProperties = new()
        {
            { "id", new JObject() { { "type", "string" }, { "title", "ID of company" } } },
            { "name", new JObject() { { "type", "string" }, { "title", "Name of company" } } },
            { "city", new JObject() { { "type", "string" }, { "title", "City of company" } } },
            { "zip", new JObject() { { "type", "string" }, { "title", "Zip Code of company" } } },
            { "street", new JObject() { { "type", "string" }, { "title", "Street of company" } } },
            { "phone", new JObject() { { "type", "string" }, { "title", "Phone Number of company" } } },
            { "email", new JObject() { { "type", "string" }, { "title", "E-Mail address of company" } } },
            { "url", new JObject() { { "type", "string" }, { "title", "URL of website" } } },
            { "countryCode", new JObject() { { "type", "string" }, { "title", "Country Code" } } },
            { "countryCode3", new JObject() { { "type", "string" }, { "title", "Country Code ISO3" } } },
            { "state", new JObject() { { "type", "string" }, { "title", "State of company" } } },
            { "stateCode", new JObject() { { "type", "string" }, { "title", "State code of company" } } },
            { "phoneCode", new JObject() { { "type", "string" }, { "title", "Phone code of company" } } },
            { "isCustomer", new JObject() { { "type", "boolean" }, { "title", "IsCustomer" } } },
            { "isFavorite", new JObject() { { "type", "boolean" }, { "title", "IsFavorite" } } },
            { "isCompetitor", new JObject() { { "type", "boolean" }, { "title", "IsCompetitor" } } },
            { "note", new JObject() { { "type", "string" }, { "title", "Note of company" } } },
            { "intro_text", new JObject() { { "type", "string" }, { "title", "Intro of company" } } },
            { "lastActivityAt", new JObject() { { "type", "string" }, { "format", "date-time" }, { "title", "Session company last activity at" } } },
            { "distance", new JObject() { { "type", "number" }, { "title", "Distance" } } },
            { "cardUrl", new JObject() { { "type", "string" }, { "title", "Card URL" } } },
            { "xingUrl", new JObject() { { "type", "string" }, { "title", "Xing URL" } } },
            { "linkedinUrl", new JObject() { { "type", "string" }, { "title", "LinkedIn URL" } } },
            {
                "sector",
                new JObject() { { "type", "object" }, { "title", "Sector" }, { "properties", new JObject(){
                    { "id", new JObject(){ { "type", "string" }, { "title", "ID" } } },
                    { "name", new JObject(){ { "type", "string" }, { "title", "Name" } } }
                } }}
            },
            {
                "category",
                new JObject() { { "type", "object" }, { "title", "Category" }, { "properties", new JObject(){
                    { "id", new JObject(){ { "type", "string" }, { "title", "ID" } } },
                    { "name", new JObject(){ { "type", "string" }, { "title", "Name" } } }
                } }}
            },
            {
                "meta",
                new JObject() { { "type", "object" }, { "title", "Meta" }, { "properties", new JObject(){
                    { "title", new JObject(){ { "type", "string" }, { "title", "Title" } } },
                    { "description", new JObject(){ { "type", "string" }, { "title", "Description" } } }
                } }}
            }
        };

        Company.Add("properties", CompanyProperties);

        //Änderungsdatum
        Company.Add("updated", "lastActivityAt");

        #endregion

        #region Visit Schema

        JObject Visit = new()
        {
            { "title", "visit" },
            { "uniqueidentifier", new JArray() { "id" } }
        };

        //Visit Eigenschaften
        JObject VisitProperties = new()
        {
            { "id", new JObject() { { "type", "integer" }, { "title", "ID of Session visit" } } },
            { "sessionGuid", new JObject() { { "type", "string" }, { "title", "GUID of Session" } } },
            { "startedAt", new JObject() { { "type", "string" }, { "format", "date-time" }, { "title", "Session visit started at" } } },
            { "lastActivityAt", new JObject() { { "type", "string" }, { "format", "date-time" }, { "title", "Session visit last activity at" } } },
            { "url", new JObject() { { "type", "string" }, { "title", "URL of the session visit" } } },
            { "referer", new JObject() { { "type", "string" }, { "title", "Referer" } } },
            { "refererMedium", new JObject() { { "type", "string" }, { "title", "Referer medium" } } },
            { "duration", new JObject() { { "type", "string" }, { "title", "Duration" } } },
            { "duration_secs", new JObject() { { "type", "integer" }, { "title", "Duration seconds" } } }
        };

        Visit.Add("properties", VisitProperties);

        //Änderungsdatum
        Visit.Add("updated", "lastActivityAt");

        #endregion

        #region Session Schema

        JObject Session = new()
        {
            { "title", "session" },
            { "uniqueidentifier", new JArray() { "guid" } }
        };

        //Session Eigenschaften
        JObject SessionProperties = new()
        {
            { "guid", new JObject() { { "type", "string" }, { "title", "GUID of Session" } } },
            { "companyID", new JObject() { { "type", "string" }, { "title", "ID of the company" } } },
            { "startedAt", new JObject() { { "type", "string" }, { "format", "date-time" }, { "title", "Session visit started at" } } },
            { "lastActivityAt", new JObject() { { "type", "string" }, { "format", "date-time" }, { "title", "Session visit last activity at" } } },
            { "duration", new JObject() { { "type", "string" }, { "title", "Duration" } } },
            { "language", new JObject() { { "type", "string" }, { "title", "Language" } } },
            {
                "referer",
                new JObject() { { "type", "object" }, { "title", "Referer" }, { "properties", new JObject(){
                    { "url", new JObject(){ { "type", "string" }, { "title", "URL" } } },
                    { "medium", new JObject(){ { "type", "string" }, { "title", "Medium" } } },
                    { "source", new JObject(){ { "type", "string" }, { "title", "Source" } } },
                    { "term", new JObject(){ { "type", "string" }, { "title", "Term" } } }
                } }}
            },
            { "campaign", new JObject() { { "type", "boolean" }, { "title", "Campaign" } } },
            { "offline_campaign", new JObject() { { "type", "boolean" }, { "title", "Offline campaign" } } },
            { "duration_secs", new JObject() { { "type", "integer" }, { "title", "Duration seconds" } } },
            { "video_url", new JObject() { { "type", "string" }, { "title", "Video URL" } } },
            { "interests", new JObject() { { "type", "array" }, { "title", "Interests" }, { "items", new JObject(){
                { "type", "object" }, { "title", "Interests" }, { "properties", new JObject(){
                    { "id", new JObject(){ { "type", "string" }, { "title", "ID" } } },
                    { "name", new JObject(){ { "type", "string" }, { "title", "Name" } } }
            } } } } } },
            { "company", new JObject() { { "type", "object" }, { "title", "Company" }, { "properties", CompanyProperties } } },
            { "visits", new JObject() { { "type", "array" }, { "title", "Visits" }, { "items", new JObject(){
                { "type", "object" }, { "title", "Visits" }, { "properties", VisitProperties } } } } }
        };

        Session.Add("properties", SessionProperties);

        //Änderungsdatum
        Session.Add("updated", "lastActivityAt");

        #endregion

        #region SessionCompany Schema - PrimaryKey ist CompanyId

        JObject SessionCompany = new()
        {
            { "title", "sessionCompany" },
            { "uniqueidentifier", new JArray() { "companyID" } },
            { "properties", SessionProperties },

            //Änderungsdatum
            { "updated", "lastActivityAt" }
        };

        #endregion

        JArray Response = new()
        {
            Company,
            Visit,
            Session,
            SessionCompany
        };

        return Response.ToString();
    }
}

2) GetData-Skript (C#)

  • Ruft https://api.salesviewer.com/sessions.json mit API-Key, Paging und optionalem From-Filter (LAST_SYNC_DATE) auf
  • Liefert je nach Helper.TargetObject nur die gefragte Menge zurück (company/visit/session/sessionCompany)
  • Duplikatschutz (Company): Da Company je Session vorkommt, wird über Pages hinweg eine Liste gesehener Company-IDs in Helper-Parametern gespeichert (COMPANY_ID), um Doppelübernahme zu verhindern
  • Einzelabruf (GETDATA_ID) ist nicht vorgesehen und wird abgewehrt
using System;
using System.Linq;
using SIS;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using SIS.Public;

public class SalesViewerGetData
{
    // SDK helper
    public SisHelper Helper { get; set; }

    // Your operation to return json data
    public string Execute()
    {
        try
        {
            JArray Result = new();

            //Einzelabfragen nicht zulässig
            if (!string.IsNullOrEmpty(Helper.GetParam("GETDATA_ID")))
                return Result.ToString();

            #region Url definieren

            string API_Key = Helper.GetParam("ApiKey");
            string Url = "https://api.salesviewer.com/sessions.json?apiKey=" + API_Key;

            if (!string.IsNullOrEmpty(Helper.GetParam("GETDATA_WHERE")))
                Url += Helper.GetParam("GETDATA_WHERE");

            //Abfrage enthält Änderungsanforderung
            if (!string.IsNullOrEmpty(Helper.GetParam("LAST_SYNC_DATE")))
                Url += "&from=" + Helper.GetParam<DateTime>("LAST_SYNC_DATE").ToString("yyyy-MM-dd HH:mm:ss");

            //Helper übergibt Page-Number (SalesViewer ist 1-basiert)
            Url += "&page=" + (Helper.Page + 1);
            Url += "&pageSize=50";

            Helper.InsertMessage("SalesViewer Url: " + Url, 5);

            #endregion

            #region Service abrufen

            string ServiceResponse = Helper.InvokeUrl(Url, "GET", null, "");

            if (!string.IsNullOrEmpty(ServiceResponse))
            {
                JObject ResponseObject = JObject.Parse(ServiceResponse);

                if (ResponseObject["result"] is JArray ResponseArray)
                    foreach (JObject SessionObject in ResponseArray)
                    {
                        #region Firma wurde angefordert

                        if (Helper.TargetObject == "company")
                        {
                            if (SessionObject.ContainsKey("company"))
                            {
                                //Firma kann mehrfach enthalten sein.
                                //Duplikate sollen über Pages hinweg vermieden werden.
                                //Helper bietet übergreifenden Storage
                                if (string.IsNullOrEmpty(Helper.GetParam("COMPANY_ID")))
                                    Helper.SetParam("COMPANY_ID", new JArray());

                                JArray CompanyIdList = JArray.Parse(Helper.GetParam("COMPANY_ID"));

                                if (SessionObject["company"] is JObject Company)
                                {
                                    string CompanyId = Company["id"]?.ToString() ?? "";

                                    if (!CompanyIdList.Any(id => id.ToString() == CompanyId))
                                    {
                                        if (Company.ContainsKey("lastActivityAt"))
                                            Company["lastActivityAt"] = SessionObject["lastActivityAt"];
                                        else
                                            Company.Add("lastActivityAt", SessionObject["lastActivityAt"]);

                                        Result.Add(Company);

                                        CompanyIdList.Add(CompanyId);
                                        Helper.SetParam("COMPANY_ID", CompanyIdList);
                                    }
                                }
                            }
                        }

                        #endregion

                        #region Visit wurde angefordert

                        if (Helper.TargetObject == "visit")
                        {
                            if (SessionObject.ContainsKey("visits"))
                            {
                                string SessionGuid = SessionObject["guid"]?.ToString() ?? "";

                                if (SessionObject["visits"] is JArray VisitArray)
                                    foreach (JObject Visit in VisitArray)
                                    {
                                        Visit.Add("sessionGuid", SessionGuid);

                                        if (Visit.ContainsKey("lastActivityAt"))
                                            Visit["lastActivityAt"] = SessionObject["lastActivityAt"];
                                        else
                                            Visit.Add("lastActivityAt", SessionObject["lastActivityAt"]);

                                        Result.Add(Visit);
                                    }
                            }
                        }

                        #endregion

                        #region Session wurde angefordert

                        if (Helper.TargetObject == "session")
                        {
                            string CompanyId = (SessionObject["company"] as JObject)?["id"]?.ToString() ?? "";

                            JObject ResultSessionObject = new(SessionObject)
                            {
                                //CompanyId aus Unterobjekt kopieren
                                { "companyID", CompanyId }
                            };

                            Result.Add(ResultSessionObject);
                        }

                        #endregion

                        #region Session mit Company-ID wurde angefordert

                        if (Helper.TargetObject == "sessionCompany")
                        {
                            string CompanyId = (SessionObject["company"] as JObject)?["id"]?.ToString() ?? "";

                            JObject ResultSessionCompany = new(SessionObject)
                            {
                                //CompanyId aus Unterobjekt kopieren
                                { "companyID", CompanyId }
                            };

                            Result.Add(ResultSessionCompany);
                        }

                        #endregion
                    }
            }

            #endregion

            return Result.ToString();
        }
        catch (Exception Exp)
        {
            Helper.InsertLog(Exp.Message, 1);
            throw;
        }
    }
}

Ergebnis

Mit dem SDK-Konnektor werden die verschachtelten SalesViewer-Antworten in flache, einzeln synchronisierbare Objektmengen zerlegt.
So lassen sich Company, Visit, Session (sowie SessionCompany) unabhängig auslesen, transformieren und in Zielsysteme schreiben – inkl. Delta-Verarbeitung und Duplikatschutz.