Revision history
Every time you change a collection or an item, a new revision is created in Etebase. Each revision is a snapshot of the collection or item at that point in time so you can easily view and rollback changes.
This is very useful if you would like to offer a change history to your users, or even if you just want to make sure your data is never lost, even if you make a mistake.
Listing revisions
Listing revisions returns normal items you can then manipulate like any others. The items are sorted newest to oldest.
- JavaScript
- Python
- Java
- Kotlin
- C/C++
- Rust
const item = await itemManager.create(
{ type: "file", },
"First draft",
);
await itemManager.batch([item]);
item.setContent("Second draft");
await itemManager.batch([item]);
const revisions = await itemManager.itemRevisions(item);
assert(revisions.data.length === 2);
// Revisions are normal items so we can use them as such
const content = await revisions.data[1].getContent();
assert(content === "First draft");
// Collections are just items, so for collection revisions:
const collectionRevisions = await itemManager.itemRevisions(collection.item);
item = item_mgr.create(
{
"type": "file",
},
b"First draft"
)
item_mgr.batch([item])
item.content = b"Second draft"
item_mgr.batch([item])
revisions = item_mgr.item_revisions(item)
assert(len(list(revisions.data)) == 2)
# Revisions are normal items so we can use them as such
content = list(revisions.data)[1].content
assert(content == b"First draft")
# Collections are just items, so for collection revisions:
col_revisions = item_mgr.item_revisions(collection.item)
ItemMetadata meta = new ItemMetadata();
meta.setType("file");
Item item = itemManager.create(meta, "First draft");
itemManager.batch(new Item[] {item});
item.setContent("Second draft");
itemManager.batch(new Item[] {item});
ItemRevisionsListResponse revisions = itemManager.itemRevisions(item);
assert(revisions.getData().length == 2);
// Revisions are normal items so we can use them as such
String content = revisions.getData()[1].getContentString();
assert(content === "First draft");
// Collections are just items, so for collection revisions:
ItemRevisionsListResponse collectionRevisions = itemManager.itemRevisions(collection.getItem());
val meta = new ItemMetadata()
meta.setType("file")
val item = itemManager.create(meta, "First draft")
itemManager.batch(arrayOf(item))
item.content = "Second draft"
itemManager.batch(arrayOf(item))
val revisions = itemManager.itemRevisions(item)
assert(revisions.data.length == 2)
// Revisions are normal items so we can use them as such
val content = revisions.data[1].contentString
assert(content === "First draft")
// Collections are just items, so for collection revisions:
val collectionRevisions = itemManager.itemRevisions(collection.item)
EtebaseItemMetadata *item_meta = etebase_item_metadata_new();
etebase_item_metadata_set_item_type(item_meta, "file");
const char item_content[] = "First draft";
EtebaseItem *item = etebase_item_manager_create(item_mgr, item_meta, item_content, strlen(item_content));
etebase_item_metadata_destroy(item_meta);
const EtebaseItem *items[] = { item };
etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL, 0, NULL);
{
const char tmp[] = "Second draft";
etebase_item_set_content(item, tmp, strlen(tmp));
const EtebaseItem *items[] = { item };
etebase_item_manager_batch(item_mgr, items, ETEBASE_UTILS_C_ARRAY_LEN(items), NULL, 0, NULL);
}
EtebaseItemRevisionsListResponse *revisions = etebase_item_manager_item_revisions(item_mgr, item, NULL);
uintptr_t list_len = etebase_item_revisions_list_response_get_data_length(revisions);
assert_int_eq(1, list_len);
{
// Revisions are normal items so we can use them as such
const EtebaseItem *list_items[list_len];
etebase_item_revisions_list_response_get_data(revisions, list_items);
char content2[100];
intptr_t content2_size = etebase_item_get_content(list_items[0], &content2, sizeof(content2));
content2[content2_size] = 0;
// This is true: content2 == "First draft"
}
// Collections are just items, so for collection revisions:
EtebaseItem *col_item = etebase_collection_as_item(col);
EtebaseItemRevisionsListResponse *revisions = etebase_item_manager_item_revisions(item_mgr, col_item, NULL);
let mut meta = ItemMetadata::new();
meta.set_item_type("file");
let mut item = item_manager.create(&meta, b"First draft")?;
item_manager.batch(vec![&item].into_iter(), None)?;
item.set_content(b"Second draft")?;
item_manager.batch(vec![&item].into_iter(), None)?;
let revisions = item_manager.item_revisions(&item, None)?;
assert_eq!(revisions.data().len(), 2);
// Revisions are normal items so we can use them as such
let content = revisions.data()[1].content()?;
assert_eq!(content, b"First draft");
// Collections are just items, so for collection revisions:
let collection_revisions = item_manager.item_revisions(&collection.item()?, None);
Iterating through revisions
Like the rest of the API, revisions can be iterated over.
- JavaScript
- Python
- Java
- Kotlin
- C/C++
- Rust
let iterator = null;
while (true) {
const revisions = await itemManager.itemRevisions(item, { iterator, limit: 5 });
iterator = revisions.iterator;
processRevisions(revisions.data);
if (revisions.done) {
break;
}
}
item_mgr = col_mgr.get_item_manager(collection)
item = ...
iterator = None
done = False
while not done:
fetch_options = FetchOptions().iterator(iterator).limit(30)
revisions = item_mgr.item_revisions(item, fetch_options)
iterator = revisions.iterator
done = revisions.done
process_revisions(revisions.data)
String iterator = null;
boolean done = false;
while (!done) {
ItemListResponse revisions = itemManager.itemRevisions(new FetchOptions().iterator(iterator).limit(30));
iterator = revisions.getIterator();
done = revisions.isDone();
processRevisions(revisions.getData());
}
var iterator: String? = null
var done = false
while (!done) {
val revisions = itemManager.itemRevisions(item, FetchOptions().iterator(iterator).limit(30))
iterator = revisions.iterator
done = revisions.isDone
processRevisions(revisions.data)
}
char *iterator = NULL;
bool done = 0;
while (!done) {
EtebaseFetchOptions *fetch_options = etebase_fetch_options_new();
etebase_fetch_options_set_iterator(fetch_options, iterator);
etebase_fetch_options_set_limit(fetch_options, 30);
EtebaseItemRevisionsListResponse *revisions = etebase_item_manager_list(item_mgr, fetch_options);
if (iterator) {
free(iterator);
}
iterator = strdup(etebase_item_revisions_list_response_get_iterator(revisions));
done = etebase_item_revisions_list_response_is_done(revisions);
uintptr_t data_len = etebase_item_revisions_list_response_get_data_length(revisions);
const EtebaseItem *data[data_len];
etebase_item_revisions_list_response_get_data(revisions, data);
process_revisions(data, data_len);
etebase_fetch_options_destroy(fetch_options);
etebase_item_revisions_list_response_destroy(revisions);
}
if (iterator) {
free(iterator);
}
let mut iterator: Option<&str> = None;
let mut revisions: IteratorListResponse<Item>;
loop {
revisions = item_manager.item_revisions(&item,
Some(&FetchOptions::new().iterator(iterator).limit(30))
)?;
iterator = revisions.iterator();
process_revisions(revisions.data());
if revisions.done() {
break
}
}