Writing Back to an API
We have created a few different ways you can write back to the API as its likely extra flexibility is needed around how the data is formatted or sent to the API endpoint.
Identifier Column
The first concept to understand is the role of the Identifier Column with Data Sync connectors. These values are attached to the row internally within Data Sync and are a way to maintain the datasource PrimaryKey even when its not part of the Schema Map. By maintaining this Identifier Column value it makes it possible to call UPDATE/DELETE actions on the target by the PrimaryKey value.
To ensure that the Identifier Column is set you need to specify the column from the Datasource which contains the Primary Key value like this within the DataTableTransform element.
<DataTableTransform path="data">
<IdentifierColumn data-type="System.Int32" path="ProductID" />
</DataTableTransform>
Default Automatic Mode
This is the default no-code option where the data is serialized to JSON and sent to the API endpoints using the URLs and Http Method(s) defined within the Writer element.
<Datasource name="products">
<Action name="GetDataSchema">
<StaticSchemaMap>
<Column name="ProductID" data-type="System.Int32" />
<Column name="ProductName" data-type="System.String" />
<Column name="SupplierID" data-type="System.Int32" />
<Column name="CategoryID" data-type="System.Int32" />
<Column name="QuantityPerUnit" data-type="System.String" />
<Column name="UnitPrice" data-type="System.Decimal" />
<Column name="UnitsInStock" data-type="System.Int32" />
<Column name="UnitsOnOrder" data-type="System.Int32" />
<Column name="ReorderLevel" data-type="System.Int32" />
<Column name="Discontinued" data-type="System.Boolean" />
</StaticSchemaMap>
</Action>
<Action name="GetDataTable">
<PagingRequest start="0" path="data">
<Fetch url="{URL}/ListApi?skip={PagingRequest.Count}&take={PageSize}">
<DataTableTransform path="data">
<IdentifierColumn data-type="System.Int32" path="ProductID" />
</DataTableTransform>
</Fetch>
</PagingRequest>
</Action>
<Writer name="Default">
<AddItem method="POST" url="{URL}/ListApi" />
<UpdateItem method="PUT" url="{URL}/ListApi/{Identifier0}" />
<DeleteItem method="DELETE" url="{URL}/ListApi/{Identifier0}" />
</Writer>
</Datasource>
For example this mode would send a HTTP response like this when Adding an item to the datasource.
POST http://localhost:2026/ListApi
Content-Type:application/json
Accept:application/json
{"ProductID":74,"ProductName":"Longlife Tofu","SupplierID":4,"CategoryID":7,"QuantityPerUnit":"5 kg pkg.","UnitPrice":10.0,"UnitsInStock":0,"UnitsOnOrder":20,"ReorderLevel":5,"Discontinued":false}
Should the API require the data within another object the you can also specify a single path element via the path attribute.
<Writer name="Default">
<AddItem method="POST" url="{URL}/ListApi" path="data" />
<UpdateItem method="PUT" url="{URL}/ListApi/{Identifier0}" path="data"/>
<DeleteItem method="DELETE" url="{URL}/ListApi/{Identifier0}" />
</Writer>
Modifying the Default
Available from Version 6.0.3344
The Default writer will only include the changed column values in UPDATE actions in the Json Body. If you need to send all the column data again you can configure the writer by setting a mode value of AllColumns this will send all column values as part of the JSON body in the request.
<Writer name="Default">
<AddItem method="POST" url="{URL}/ListApi" />
<UpdateItem method="PUT" url="{URL}/ListApi/{Identifier0}" mode="AllColumns" />
<DeleteItem method="DELETE" url="{URL}/ListApi/{Identifier0}" />
</Writer>
To send only the Key column values from the Schema Mapping as part of the JSON body for UPDATE set the mode to AllKeys.
<Writer name="Default">
<AddItem method="POST" url="{URL}/ListApi" />
<UpdateItem method="PUT" url="{URL}/ListApi/{Identifier0}" mode="AllKeys" />
<DeleteItem method="DELETE" url="{URL}/ListApi/{Identifier0}" />
</Writer>
To send only the Identifier column values set the mode to AllIdentifiers and provide a comma separated list of column names in the identifier-columns attribute to use for the Identifier column values.
<Writer name="Default">
<AddItem method="POST" url="{URL}/ListApi" />
<UpdateItem method="PUT" url="{URL}/ListApi/{Identifier0}" mode="AllIdentifiers" identifier-columns="ProductID" />
<DeleteItem method="DELETE" url="{URL}/ListApi/{Identifier0}" />
</Writer>
Project Automation Callbacks
This mode allows for full control of how the API is called via a C# delegate callback on each item. In this mode we setup the Writer element with a name of Callback and then configure the callback function in Project Automation.
<Datasource name="products">
<Action name="GetDataSchema">
<StaticSchemaMap>
<Column name="ProductID" data-type="System.Int32" />
<Column name="ProductName" data-type="System.String" />
<Column name="SupplierID" data-type="System.Int32" />
<Column name="CategoryID" data-type="System.Int32" />
<Column name="QuantityPerUnit" data-type="System.String" />
<Column name="UnitPrice" data-type="System.Decimal" />
<Column name="UnitsInStock" data-type="System.Int32" />
<Column name="UnitsOnOrder" data-type="System.Int32" />
<Column name="ReorderLevel" data-type="System.Int32" />
<Column name="Discontinued" data-type="System.Boolean" />
</StaticSchemaMap>
</Action>
<Action name="GetDataTable">
<PagingRequest start="0" path="data">
<Fetch url="{URL}/ListApi?skip={PagingRequest.Count}&take={PageSize}">
<DataTableTransform path="data">
<IdentifierColumn data-type="System.Int32" path="ProductID" />
</DataTableTransform>
</Fetch>
</PagingRequest>
</Action>
<Writer name="Callback">
<AddItem url="{URL}/ListApi" />
<UpdateItem url="{URL}/ListApi/{Identifier0}" />
<DeleteItem url="{URL}/ListApi/{Identifier0}" />
</Writer>
</Datasource>
You would then enable Project Automation and within the Start() method setup the callback methods for ADD/UPDATE/DELETE. These will then be called during the synchronisation process for you to call the API.
For convenience we pass you an object for calling HTTP methods, the HttpWebRequestHelper, with any authentication headers set. Within the ProjectAutomationItemInfo object we have the target URL in the Target property.
The Data property contains a Dictionary<string, object> of the data converted to match the target schema.
Therefore a simple implementation which would match the Default mode would be implemented like this.
public override void Start()
{
DataSourceB.SetWriterCallback(AddItem, UpdateItem, DeleteItem);
}
public void AddItem(HttpWebRequestHelper helper, ProjectAutomationItemInfo info)
{
helper.PostRequestAsJson(info.Data, info.Target);
}
public void UpdateItem(HttpWebRequestHelper helper, ProjectAutomationItemInfo info)
{
helper.PutRequestAsJson(info.Data, info.Target);
}
public void DeleteItem(HttpWebRequestHelper helper, ProjectAutomationItemInfo info)
{
helper.DeleteRequestAsJson(null, info.Target);
}
Project Automation Batch Callback
Available from Data Sync Version 6.0.3398
This mode allows for full control of how the API is called in batches via a C# delegate callback on each batch of items. In this mode we setup the Writer element with a name of BatchCallback and then configure the callback functions in Project Automation.
In this example the API expects an Array of items like this below. Therefore we will need to wrap the array of results into an anonymous object to add the items element.
{
"items": [
{ ... },
{ ... }
]
}
Since we need to include the item identifier in the body of the request rather than the URL we use the mode option of AllIdentifiers to ensure that the items ID is included in UPDATE and DELETE operations.
To Adjust the size of the Batch include a Parameter named UpdateBatchSize where you can specify the size of each batch of items.
<Connector name="Simego Web API Connector" description="Connect to Simego Web API" version="1.0">
<Parameters>
<Parameter name="URL" value="http://localhost:8080" />
<Parameter name="PageSize" value="10" />
<Parameter name="UpdateBatchSize" value="10" />
</Parameters>
<Datasources>
<Datasource name="Products">
<Action name="GetDataSchema">
<StaticSchemaMap>
<Column name="ProductID" data-type="System.Int32" />
<Column name="ProductName" data-type="System.String" />
<Column name="SupplierID" data-type="System.Int32" />
<Column name="CategoryID" data-type="System.Int32" />
<Column name="QuantityPerUnit" data-type="System.String" />
<Column name="UnitPrice" data-type="System.Decimal" />
<Column name="UnitsInStock" data-type="System.Int32" />
<Column name="UnitsOnOrder" data-type="System.Int32" />
<Column name="ReorderLevel" data-type="System.Int32" />
<Column name="Discontinued" data-type="System.Boolean" />
</StaticSchemaMap>
</Action>
<Action name="GetDataTable">
<PagingRequest path="data" start="1" next-path="nextpage">
<Fetch url="{URL}/app/list/{Datasource.Name}?limit={PageSize}&page={PagingRequest.Page}">
<DataTableTransform path="data">
<IdentifierColumn data-type="System.Int32" path="WS_ID" />
</DataTableTransform>
</Fetch>
</PagingRequest>
</Action>
<Writer name="BatchCallback">
<AddItem url="{URL}/app/list/{Datasource.Name}" />
<UpdateItem url="{URL}/app/list/{Datasource.Name}" mode="AllIdentifiers" identifier-columns="WS_ID" />
<DeleteItem url="{URL}/app/list/{Datasource.Name}" mode="AllIdentifiers" identifier-columns="WS_ID" />
</Writer>
</Datasource>
</Datasources>
</Connector>
Then in project automation we can use:
public override void Start()
{
DataSourceB.SetWriterBatchCallback(AddItem, UpdateItem, DeleteItem);
}
public void AddItem(HttpWebRequestHelper helper, ProjectAutomationBatchInfo info)
{
helper.PostRequestAsJson(new { items = info.Data }, info.Target);
}
public void UpdateItem(HttpWebRequestHelper helper, ProjectAutomationBatchInfo info)
{
helper.PutRequestAsJson(new { items = info.Data }, info.Target);
}
public void DeleteItem(HttpWebRequestHelper helper, ProjectAutomationBatchInfo info)
{
helper.DeleteRequestAsJson(new { items = info.Data }, info.Target);
}
Since we are processing items in Batch mode, this does not support Project Automation Item events. If you need to do further processing we would suggest you do that in the callback function instead.
If you have any questions or issues when trying to write back to your API, please reach out to us via the normal support email.