Entwicklung eines SDK-basierten Syncler-Systems
Mit dem Konnektor „SDK“ können Sie in Syncler eigene Systeme per C#-Skripten implementieren. Die Skripte werden zur Laufzeit kompiliert und ausgeführt und bilden die Brücke zu externen Diensten/Apps. Aus fertig konfigurierten SDK-Systemen lassen sich System-Vorlagen erstellen, die wiederverwendbar sind und zentral aktualisiert werden können.
Architektur & Bausteine
Ein SDK-System besteht aus bis zu vier voneinander unabhängigen Skripten:
- Schema-Skript – beschreibt die verfügbaren Schemaobjekte (Tabellen/Entitäten) als JSON-Schema.
- Lese-Skript – liefert Daten für ein Schemaobjekt (inkl. Delta-/Seitenabruf).
- Schreib-Skript – schreibt/aktualisiert/löscht Datensätze für ein Schemaobjekt.
- Abfrage-Skript – führt freie Abfragen (Query) aus und liefert Schema + Daten.
Die Skripte teilen keine gemeinsame Codebasis. Gemeinsamer Kontext ist der SDK-Helper (siehe unten), der System-/Sync-Parameter, Paging-Infos, Logging-Methoden und Utility-Funktionen bereitstellt.
Unterstützte Libraries
- .NET-Basis (eingeschränkt, je nach Cloud/On-Prem).
- Newtonsoft.Json (JObject, JArray, Serialisierung) – vom Helper ebenfalls genutzt.
- Eigene Entwicklung in Visual Studio ist möglich; binden Sie dafür SynclerPublic.dll ein und übertragen Sie den Code anschließend in die Oberfläche.
Datenformat
- Ein- und Ausgaben erfolgen durchgängig als JSON (Strings).
- Skripte liefern i. d. R.
JArrayvonJObject(als String).
Der SDK-Helper (Kurzüberblick)
Der Helper steht in jedem Skript zur Verfügung und stellt u. a. bereit:
- Kontext-/Steuerwerte:
Helper.TargetObject,Helper.Page,Helper.Statement,Helper.DeleteSetObject, … - Objektzugriffe:
Helper.GetObject(Quelle im Datensatzskript),Helper.SetObject/Helper.SetObjectChanges(Ziel im Schreibskript),Helper.SourceSchemaObject/Helper.TargetSchemaObject. - Parameter-Store:
GetParam(...),SetParam(...)(serialisierbar; über Skriptaufrufe/Paging hinweg). - HTTP-Call:
InvokeUrl(url, method, header, data, base64, contentType). - Logging:
InsertMessage(text, level)undInsertLog(text, level)inkl. Überladung mit Objekt (z. B.ScriptLog).
Eine ausführliche Auflistung finden Sie in der separaten SDK-Helper-Dokumentation.
1) Schema-Skript
Das Schema-Skript liefert ein JSON-Array von Schemaobjekten (als String). Die Notation orientiert sich an JSON-Schema (https://json-schema.org/).
Objekteigenschaften
title(string, Pflicht) – eindeutiger Name des Objekts.uniqueidentifier(string[]) – Namen der ID-Felder (auf oberster Ebene).properties(object, Pflicht) – Felddefinitionen (siehe unten).required(string[]) – Pflichtfelder des Objekts.updated(string) – Feldname für „Änderungsinformation“ (Delta-Feld).
Feldeigenschaften (Auszug)
type:string(Default),number,integer,boolean,object,arrayobject→ Objektgruppe (kein Feld);array→ Liste/Objektgruppe (items= Schema des Kindelements, optionalischildlistfür Positionslisten).
title– Anzeigename.description– Hilfetext (u. a. in Feldzuordnung sichtbar).readOnly– Feld ist schreibgeschützt.format– z. B.date-time,date,object(JSON im String),array(JSON-Array im String).maxLength– maximale Länge für Strings.enum– Werteliste für Auswahldaten.
Beispiel
[
{
"title": "address",
"properties": {
"street": {
"title": "Street",
"type": "string",
"maxLength": 60,
"description": "Hint for user"
},
"housenumber": {
"type": "integer",
"title": "Housenumber"
},
"addressid": {
"type": "integer",
"title": "Record ID",
"readOnly": true
},
"updateddate": {
"title": "Updated at",
"type": "string",
"format": "date-time",
"description": "Last updated date"
},
"country": {
"title": "Land",
"type": "string",
"enum": ["DE", "AT", "CH"]
},
"geo": {
"title": "Coordinates",
"type": "object",
"properties": {
"lat": { "title": "Latitude", "type": "number" },
"lng": { "title": "Longitude", "type": "number" }
}
},
"floors": {
"title": "Floors",
"type": "array",
"ischildlist": false,
"items": {
"title": "Floors",
"type": "object",
"properties": {
"appartments": { "title": "Appartments", "type": "number" },
"renter": { "title": "Renter", "type": "number" }
}
}
},
"colors": {
"title": "Colors",
"type": "array"
}
},
"required": ["street"],
"updated": "updateddate",
"uniqueidentifier": ["addressid"]
}
]
Hinweis – Nachverarbeitung & OAuth
Nach Ausführung des Schema-Skripts wird das System intern erneut gespeichert. Hier können Sie z. B. OAuth-Tokens initialisieren/aktualisieren:
string oAuthCode = Helper.GetParam("OAuthCode"); string authorizationHeader = Helper.GetParam("OAuthClientId") + ":" + Helper.GetParam("OAuthSecret"); // ... Token holen/erneuern ... string refreshToken = TokenObject["refresh_token"].ToString(); Helper.SaveParameter(new SisParam { Name = "MyRefreshToken", Value = refreshToken }); // Code zurücksetzen Helper.SetParam("OAuthCode", "");
2) Lese-Skript
Das Lese-Skript liefert passend zu Helper.TargetObject Datensätze als JArray (String). Wichtige Parameter:
Helper.TargetObject– Schemaobjektname der Anforderung.Helper.SourceSchemaObject– Schema alsJObject(optional nutzbar).- Delta/Paging:
Helper.Page– aktuelle Seite (0-basiert).Helper.GetParam("LAST_SYNC_DATE")/Helper.GetParam("LAST_SYNC_VERSION")– Grenzwerte.- Max. 50 Page-Aufrufe je Anfrage.
- Einzel/Liste:
Helper.GetParam("GETDATA_ID")– Einzelabruf.Helper.GetParam("GETDATA_WHERE")– Filterbedingung.
Persistente Laufzeit-Daten (über Paging hinweg)
Nutzen Sie GetParam/SetParam mit serialisierbaren Typen (z. B. JArray) um prozessweite Daten (z. B. Duplikatlisten) zu halten:
if (string.IsNullOrEmpty(Helper.GetParam("COMPANY_ID"))) Helper.SetParam("COMPANY_ID", new JArray()); JArray companyIdList = JArray.Parse(Helper.GetParam("COMPANY_ID")); // ... bearbeiten ... Helper.SetParam("COMPANY_ID", companyIdList);
Minimalbeispiel (vereinfacht)
public class MyReader { public SisHelper Helper { get; set; } public string Execute() { var result = new JArray(); string obj = Helper.TargetObject; // z. B. "address" int page = Helper.Page; // 0,1,2,... string id = Helper.GetParam("GETDATA_ID"); // optional string where = Helper.GetParam("GETDATA_WHERE"); // optional DateTime? from = Helper.GetParamOrNull<DateTime>("LAST_SYNC_DATE"); // 1) URL/Query zusammenstellen (inkl. Paging/Delta) // 2) Remote-Service abrufen (Helper.InvokeUrl) // 3) Response in result (JArray<JObject>) schreiben return result.ToString(); } }
3) Schreib-Skript
Das Schreib-Skript erhält das Zielobjekt als Helper.SetObject (vollständig) und – sofern vorhanden – tatsächliche Änderungen als Helper.SetObjectChanges (reduziert). Das Ziel-Schema steht in Helper.TargetSchemaObject. Löschanforderungen erkennen Sie an Helper.DeleteSetObject == true.
Wichtig: Syncler erwartet als Rückgabe das gespeicherte Objekt (inkl. ID und neuer Änderungsinfo). Nur so können Datensatz-Abbildungen/Delta korrekt funktionieren.
Minimalbeispiel (vereinfacht)
public class MyWriter { public SisHelper Helper { get; set; } public string Execute() { var target = (JObject)Helper.SetObject ?? new JObject(); var changes = (JObject)Helper.SetObjectChanges; // kann null sein bool delete = Helper.DeleteSetObject; if (delete) { // DELETE auf Remote-System // ... // Rückgabe minimal mit ID return new JObject { ["id"] = target["id"] }.ToString(); } else { // INSERT/UPDATE auf Remote-System (bevorzugt changes verwenden) // ... // Antwort des Systems (inkl. ID/Updated) zurückgeben: var saved = new JObject(target) { ["id"] = target["id"] ?? "12345", ["updated"] = DateTime.UtcNow.ToString("o") }; return saved.ToString(); } } }
4) Abfrage-Skript (Query)
Das Abfrage-Skript wird sowohl für Schema- als auch für Datenabruf genutzt. Unterscheidung z. B. über:
bool isSchemaRequest = Helper.GetParam<bool>("GetQuerySchema");
- Statement:
Helper.Statemententhält das Abfrage-Statement (frei definierbar; kann auch CSV/JSON beschreiben). - Daten-Antwort:
JArrayflacherJObject-Zeilen (als String). - Schema-Antwort:
JArrayvon Spaltendefinitionen.
Beispiel – Schemaantwort
JArray schemaColumns = new JArray { new JObject { ["Name"] = "referenceType", ["ColumnType"] = "String" }, new JObject { ["Name"] = "trackingNumber", ["ColumnType"] = "String" }, new JObject { ["Name"] = "eta", ["ColumnType"] = "Datetime" }, new JObject { ["Name"] = "vesselName", ["ColumnType"] = "String" } }; return schemaColumns.ToString();
Nachrichten & Protokolle
Zur Nachvollziehbarkeit sollten Sie Meldungen (flüchtig) und Protokolle (persistiert) setzen:
// UI-Nachricht (Broadcast, begrenzter Stack) Helper.InsertMessage("Start page 3", 5); // 1=Error, 2=Warning, 3=Message, 4=Feedback, 5=Debug // Persistentes Protokoll Helper.InsertLog("Wrote 50 records", 3); // Objekt-Variante (z. B. ScriptLog mit Record-Bezug) Helper.InsertLog(new ScriptLog { RecordType = "address", RecordId = "A-123", LogMessage = "Updated" });
Hinweis: Fehler-Logs führen automatisch zu fehlgeschlagenen Ausführungen im Sync.
Externe Services aufrufen
Nutzen Sie InvokeUrl(...) für HTTP-Aufrufe:
// GET mit Header var headers = new JObject { ["Authorization"] = "Bearer " + Helper.GetParam("AccessToken") }; string json = Helper.InvokeUrl("https://api.example.com/items?page=1", "GET", headers, null); // POST mit JSON-Body var payload = new JObject { ["name"] = "Test", ["active"] = true }; string resp = Helper.InvokeUrl( "https://api.example.com/items", "POST", headers, payload.ToString(), false, "application/json" ); // Binär als Base64 holen string pdfBase64 = Helper.InvokeUrl("https://api.example.com/doc.pdf", "GET", null, null, true, "application/pdf");
Parameter:
InvokeUrl(string url, string method, JObject? header, string data, bool base64 = false, string contentType = "application/json")
Best Practices
- Delta & Paging: Definieren Sie im Schema ein
updated-Feld und nutzen SieLAST_SYNC_DATE/LAST_SYNC_VERSION. Paginieren Sie mitHelper.Page. - Serialisierbare Param-Daten: Für prozessweite Zustände (z. B. Duplikatlisten) JArray/JObject statt .NET-Listen verwenden.
- Fehlerrobustheit: Exceptions stets loggen (
InsertLog) und aussagekräftige Meldungen setzen. - Rückgaben vollständig: Schreib-Skript muss ID + Änderungsinfo zurückgeben, sonst funktionieren Datensatz-Abbildungen/Delta nicht.
Kurzer Implementierungs-Workflow
- SDK-System anlegen und Parameter (z. B. Endpunkte, Keys) definieren.
- Schema-Skript erstellen und testen (Objekte, IDs,
updated). - Lese-/Schreib-Skripte umsetzen (Delta, Paging, Changes, Delete).
- Abfrage-Skript (optional) für besondere Reports/Lookups.
- Sync(s) konfigurieren: Quelle/Ziel, Feldzuordnung, Transformation, Konfliktverhalten.
- Testlauf (manuell/Adhoc), Protokolle prüfen, Grenzwerte setzen.
- Vorlage erzeugen (optional, System-Template), um Deployments zu standardisieren.