Synchronization
They synchronization endpoints allows you to sync all of your content to store it locally. You can choose to sync everything, or just assets, certain content types or deletions.
The initial sync
To start syncing content you need to call the SyncInitial
method. If you call it without any parameters you sync every content type and all assets, but you could also specify
that you wish to sync only a specific content type or only assets or deletions.
var result = await client.SyncInitial();
//Sync everything
var result = await client.SyncInitial(SyncType.Asset);
//Sync only assets
var result = await client.SyncInitial(SyncType.Entry)
//Sync only entries
var result = await client.SyncInitial(SyncType.Entry, "123")
//Sync only entries of the "123" content type
var result = await client.SyncInitial(SyncType.Deletions)
//Sync only deletions of assets and entries
var result = await client.SyncInitial(SyncType.DeletedAsset)
//Sync only deleted assets
var result = await client.SyncInitial(SyncType.DeletedEntry)
//Sync only deleted entries
Handling the result of a synchronization
All sync operations return a SyncResult
. This class contains all assets, entries and deletions that the synchronization call resulted in and also contains two URL properties.
NextSyncUrl and NextPageUrl. NextPageUrl is only present if there is more content to be recieved for the current sync operation, NextSyncUrl will be null in this case.
If there is no more content for the current sync only NextSyncUrl will be present.
var res = await client.SyncInitial();
//Sync everything
var syncedEntries = res.Entries;
//An IEnumerable of Entry<dynamic>
var syncedAssets = res.Assets;
//An IEnumerable of SyncedAsset
var deletedEntries = res.DeletedEntries;
//An IEnumerable of SystemProperty
var deletedEntries = res.DeletedAssets;
//An IEnumerable of SystemProperty
Note that the synced entries are of the type Entry<dynamic>
. This is because the JSON returned by a sync must contain every locale of a space. The JSON structure of an entry would
look something like this.
"fields": {
"productName": {
"en-US": "SoSo Wall Clock",
"sv": "SåSå Väggklocka"
},
"slug": {
"en-US": "soso-wall-clock"
},
"productDescription": {
"en-US": "The newly released SoSo Clock from Lemnos..."
},
"sizetypecolor": {
"en-US": "10\" x 2.2\""
}
}
This poses a problem when deserializing as we cannot have C# members that include hyphens (-) in their name. Furthermore we would probably not want to create classes that have a structure
like this anyway, a product class with a property ProductName
which is an object with two more properties, one for each language. To work around this for sync operations the Entry<dynamic>
is used. This means that you can access any property in any language by using an indexer.
var res = await client.SyncInitial();
Console.WriteLine(res.Entries.First().Fields.productName["en-US"].ToString()); // => SoSo Wall Clock
Console.WriteLine(res.Entries.First().Fields.productName.sv.ToString()); // => SåSå Väggklocka
This is also the reason for not using the Asset
class directly for synced assets, but rather the SyncedAsset
class.
var res = await client.SyncInitial();
Console.WriteLine(res.Assets.First().Fields.title["en-US"].ToString()); // => SoSo Wall Clock
Console.WriteLine(res.Assets.First().Fields.title.sv.ToString()); // => SåSå Väggklocka
Syncing the next page or result
After a sync completes and you receive your SyncResult
you would normally store the NextSyncUrl somewhere to be able to call it when it is time to fetch changes. To do that you simply
pass either the full URL or the synctoken directly to the SyncNextResult
method.
var res = await client.SyncInitial();
if(!string.IsNullOrEmpty(res.NextSyncUrl)){
//store the sync url somewhere
db.Store(res.NextSyncUrl);
}
//Sometime later it's time to fetch the syncUrl from storage and fetch the next SyncResult
var syncUrl = db.RetrieveSyncUrl();
var res = await client.SyncNextResult(syncUrl);
//Process the result and again store the NextSyncUrl and sync again at the next interval
if(!string.IsNullOrEmpty(res.NextSyncUrl)){
//store the sync url somewhere
db.Store(res.NextSyncUrl);
}
It is important to note that Contentful makes no assumption on the interval of your sync operations. That is up to the individual application to decide. You could sync every 10 minutes or every other week. The syncresult will contain all changes in your space since the last sync operation.
If you lose it there's no retrieveing it and you would have to redo an initial sync.
Recursively syncing all pages
If you wish to make sure the initial sync retrieves all pages during the intial sync you can use the SyncInitialRecursive
method.
var res = await client.SyncInitialRecursive();
//Sync everything and recursively process any NextPageUrls
var syncedEntries = res.Entries;
//An IEnumerable of Entry<dynamic> containing all entries for all pages aggregated into a single collection.
var syncedAssets = res.Assets;
//An IEnumerable of SyncedAsset containing all assets for all pages aggregated into a single collection.
var deletedEntries = res.DeletedEntries;
//An IEnumerable of SystemProperty containing all deleted entries for all pages aggregated into a single collection.
var deletedEntries = res.DeletedAssets;
//An IEnumerable of SystemProperty containing all deleted assets for all pages aggregated into a single collection.
It is very important to understand that this potentially results in multiple calls to the sync endpoint at Contentful. It is also very possible if you have a large number of pages that
the collection of entries and assets become very large. In those situations it might be better to manually process each page in turn using the SyncInitial
and SyncNextResult
methods.