C Requires Chilkat v11.5.0+
C
Box: Poll User Events and Save a Checkpoint
See more Box Examples
Poll the authenticated user event stream in bounded batches. The local JSON checkpoint keeps processed event IDs and the next stream position. This single-process demonstration logs events; a production worker should commit its business action, deduplication record, and cursor transactionally and prune old IDs.
See the Box API guide for setup and the complete workflow.
Chilkat C Downloads
#include <C_CkHttpCurl.h>
#include <C_CkJsonObject.h>
#include <C_CkFileAccess.h>
#include <C_CkStringBuilder.h>
void ChilkatSample(void)
{
BOOL success;
HCkHttpCurl curl;
HCkJsonObject state;
HCkFileAccess files;
const char *statePath;
HCkJsonObject page;
HCkStringBuilder seenPath;
const char *eventId;
const char *nextPosition;
int batch;
int i;
int count;
BOOL more;
HCkStringBuilder authorizedCommand1;
int headerReplacement1;
success = FALSE;
// Requires Chilkat 11.5.0 or later and a previously unlocked Chilkat bundle.
// See Global Unlock Sample.
// Guide: Box API Guide.
success = FALSE;
// Supply an unexpired token obtained with one of the Box authentication examples.
// A token represents a particular user or service account. IDs must be accessible to it.
curl = CkHttpCurl_Create();
CkHttpCurl_SetVar(curl,"access_token","BOX_ACCESS_TOKEN");
state = CkJsonObject_Create();
files = CkFileAccess_Create();
statePath = "qa_data/box_events_state.json";
if (CkFileAccess_FileExists(files,statePath) == TRUE) {
success = CkJsonObject_LoadFile(state,statePath);
if (success != TRUE) {
printf("%s\n",CkJsonObject_lastErrorText(state));
CkHttpCurl_Dispose(curl);
CkJsonObject_Dispose(state);
CkFileAccess_Dispose(files);
return;
}
if (CkJsonObject_HasMember(state,"position") != TRUE) {
printf("Invalid checkpoint: position missing.\n");
CkHttpCurl_Dispose(curl);
CkJsonObject_Dispose(state);
CkFileAccess_Dispose(files);
return;
}
}
else {
// "now" starts a new subscription. It deliberately skips historical events.
CkJsonObject_UpdateString(state,"position","now");
}
CkHttpCurl_SetVar(curl,"position",CkJsonObject_stringOf(state,"position"));
page = CkJsonObject_Create();
seenPath = CkStringBuilder_Create();
batch = 0;
more = TRUE;
while ((more == TRUE) && (batch < 20)) {
CkHttpCurl_ClearTargetOutput(curl,"*");
authorizedCommand1 = CkStringBuilder_Create();
CkStringBuilder_SetString(authorizedCommand1,"curl -X GET \"https://api.box.com/2.0/events?stream_type=all&stream_position={{position}}&limit=100\" -H \"Authorization: Bearer {{access_token}}\"");
headerReplacement1 = CkStringBuilder_Replace(authorizedCommand1,"{{access_token}}",CkHttpCurl_getVar(curl,"access_token"));
success = CkHttpCurl_DoYourThing(curl,CkStringBuilder_getAsString(authorizedCommand1));
if ((success != TRUE) || (CkHttpCurl_getStatusCode(curl) != 200)) {
printf("Box request failed. HTTP status: %d\n",CkHttpCurl_getStatusCode(curl));
printf("Failure reason: %d\n",CkHttpCurl_getFailReason(curl));
printf("%s\n",CkHttpCurl_responseBodyStr(curl));
CkHttpCurl_Dispose(curl);
CkJsonObject_Dispose(state);
CkFileAccess_Dispose(files);
CkJsonObject_Dispose(page);
CkStringBuilder_Dispose(seenPath);
CkStringBuilder_Dispose(authorizedCommand1);
return;
}
success = CkHttpCurl_GetResponseJson(curl,page);
if (success != TRUE) {
printf("%s\n",CkHttpCurl_lastErrorText(curl));
CkHttpCurl_Dispose(curl);
CkJsonObject_Dispose(state);
CkFileAccess_Dispose(files);
CkJsonObject_Dispose(page);
CkStringBuilder_Dispose(seenPath);
CkStringBuilder_Dispose(authorizedCommand1);
return;
}
count = CkJsonObject_SizeOfArray(page,"entries");
i = 0;
while (i < count) {
CkJsonObject_putI(page,i);
eventId = CkJsonObject_stringOf(page,"entries[i].event_id");
if (eventId == "") {
printf("Event without an event_id; checkpoint not advanced.\n");
CkHttpCurl_Dispose(curl);
CkJsonObject_Dispose(state);
CkFileAccess_Dispose(files);
CkJsonObject_Dispose(page);
CkStringBuilder_Dispose(seenPath);
CkStringBuilder_Dispose(authorizedCommand1);
return;
}
CkStringBuilder_Clear(seenPath);
CkStringBuilder_Append(seenPath,"seen.");
CkStringBuilder_Append(seenPath,eventId);
if (CkJsonObject_HasMember(state,CkStringBuilder_getAsString(seenPath)) == FALSE) {
// Replace this logging with your idempotent business operation.
// Record success only after that operation succeeds.
printf("%s %s %s\n",eventId,CkJsonObject_stringOf(page,"entries[i].event_type"),CkJsonObject_stringOf(page,"entries[i].source.id"));
CkJsonObject_UpdateBool(state,CkStringBuilder_getAsString(seenPath),TRUE);
}
i = i + 1;
}
nextPosition = CkJsonObject_stringOf(page,"next_stream_position");
if (nextPosition == "") {
printf("Missing next_stream_position; checkpoint not advanced.\n");
CkHttpCurl_Dispose(curl);
CkJsonObject_Dispose(state);
CkFileAccess_Dispose(files);
CkJsonObject_Dispose(page);
CkStringBuilder_Dispose(seenPath);
CkStringBuilder_Dispose(authorizedCommand1);
return;
}
more = (count > 0) && (nextPosition != CkHttpCurl_getVar(curl,"position"));
CkJsonObject_UpdateString(state,"position",nextPosition);
success = CkJsonObject_WriteFile(state,statePath);
if (success != TRUE) {
printf("%s\n",CkJsonObject_lastErrorText(state));
CkHttpCurl_Dispose(curl);
CkJsonObject_Dispose(state);
CkFileAccess_Dispose(files);
CkJsonObject_Dispose(page);
CkStringBuilder_Dispose(seenPath);
CkStringBuilder_Dispose(authorizedCommand1);
return;
}
CkHttpCurl_SetVar(curl,"position",nextPosition);
batch = batch + 1;
}
printf("Checkpoint saved. Run again later to continue polling.\n");
CkHttpCurl_Dispose(curl);
CkJsonObject_Dispose(state);
CkFileAccess_Dispose(files);
CkJsonObject_Dispose(page);
CkStringBuilder_Dispose(seenPath);
CkStringBuilder_Dispose(authorizedCommand1);
}