Unicode C Requires Chilkat v11.5.0+
Unicode 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 Unicode C Downloads
#include <C_CkHttpCurlW.h>
#include <C_CkJsonObjectW.h>
#include <C_CkFileAccessW.h>
#include <C_CkStringBuilderW.h>
void ChilkatSample(void)
{
BOOL success;
HCkHttpCurlW curl;
HCkJsonObjectW state;
HCkFileAccessW files;
const wchar_t *statePath;
HCkJsonObjectW page;
HCkStringBuilderW seenPath;
const wchar_t *eventId;
const wchar_t *nextPosition;
int batch;
int i;
int count;
BOOL more;
HCkStringBuilderW 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 = CkHttpCurlW_Create();
CkHttpCurlW_SetVar(curl,L"access_token",L"BOX_ACCESS_TOKEN");
state = CkJsonObjectW_Create();
files = CkFileAccessW_Create();
statePath = L"qa_data/box_events_state.json";
if (CkFileAccessW_FileExists(files,statePath) == TRUE) {
success = CkJsonObjectW_LoadFile(state,statePath);
if (success != TRUE) {
wprintf(L"%s\n",CkJsonObjectW_lastErrorText(state));
CkHttpCurlW_Dispose(curl);
CkJsonObjectW_Dispose(state);
CkFileAccessW_Dispose(files);
return;
}
if (CkJsonObjectW_HasMember(state,L"position") != TRUE) {
wprintf(L"Invalid checkpoint: position missing.\n");
CkHttpCurlW_Dispose(curl);
CkJsonObjectW_Dispose(state);
CkFileAccessW_Dispose(files);
return;
}
}
else {
// "now" starts a new subscription. It deliberately skips historical events.
CkJsonObjectW_UpdateString(state,L"position",L"now");
}
CkHttpCurlW_SetVar(curl,L"position",CkJsonObjectW_stringOf(state,L"position"));
page = CkJsonObjectW_Create();
seenPath = CkStringBuilderW_Create();
batch = 0;
more = TRUE;
while ((more == TRUE) && (batch < 20)) {
CkHttpCurlW_ClearTargetOutput(curl,L"*");
authorizedCommand1 = CkStringBuilderW_Create();
CkStringBuilderW_SetString(authorizedCommand1,L"curl -X GET \"https://api.box.com/2.0/events?stream_type=all&stream_position={{position}}&limit=100\" -H \"Authorization: Bearer {{access_token}}\"");
headerReplacement1 = CkStringBuilderW_Replace(authorizedCommand1,L"{{access_token}}",CkHttpCurlW_getVar(curl,L"access_token"));
success = CkHttpCurlW_DoYourThing(curl,CkStringBuilderW_getAsString(authorizedCommand1));
if ((success != TRUE) || (CkHttpCurlW_getStatusCode(curl) != 200)) {
wprintf(L"Box request failed. HTTP status: %d\n",CkHttpCurlW_getStatusCode(curl));
wprintf(L"Failure reason: %d\n",CkHttpCurlW_getFailReason(curl));
wprintf(L"%s\n",CkHttpCurlW_responseBodyStr(curl));
CkHttpCurlW_Dispose(curl);
CkJsonObjectW_Dispose(state);
CkFileAccessW_Dispose(files);
CkJsonObjectW_Dispose(page);
CkStringBuilderW_Dispose(seenPath);
CkStringBuilderW_Dispose(authorizedCommand1);
return;
}
success = CkHttpCurlW_GetResponseJson(curl,page);
if (success != TRUE) {
wprintf(L"%s\n",CkHttpCurlW_lastErrorText(curl));
CkHttpCurlW_Dispose(curl);
CkJsonObjectW_Dispose(state);
CkFileAccessW_Dispose(files);
CkJsonObjectW_Dispose(page);
CkStringBuilderW_Dispose(seenPath);
CkStringBuilderW_Dispose(authorizedCommand1);
return;
}
count = CkJsonObjectW_SizeOfArray(page,L"entries");
i = 0;
while (i < count) {
CkJsonObjectW_putI(page,i);
eventId = CkJsonObjectW_stringOf(page,L"entries[i].event_id");
if (eventId == L"") {
wprintf(L"Event without an event_id; checkpoint not advanced.\n");
CkHttpCurlW_Dispose(curl);
CkJsonObjectW_Dispose(state);
CkFileAccessW_Dispose(files);
CkJsonObjectW_Dispose(page);
CkStringBuilderW_Dispose(seenPath);
CkStringBuilderW_Dispose(authorizedCommand1);
return;
}
CkStringBuilderW_Clear(seenPath);
CkStringBuilderW_Append(seenPath,L"seen.");
CkStringBuilderW_Append(seenPath,eventId);
if (CkJsonObjectW_HasMember(state,CkStringBuilderW_getAsString(seenPath)) == FALSE) {
// Replace this logging with your idempotent business operation.
// Record success only after that operation succeeds.
wprintf(L"%s %s %s\n",eventId,CkJsonObjectW_stringOf(page,L"entries[i].event_type"),CkJsonObjectW_stringOf(page,L"entries[i].source.id"));
CkJsonObjectW_UpdateBool(state,CkStringBuilderW_getAsString(seenPath),TRUE);
}
i = i + 1;
}
nextPosition = CkJsonObjectW_stringOf(page,L"next_stream_position");
if (nextPosition == L"") {
wprintf(L"Missing next_stream_position; checkpoint not advanced.\n");
CkHttpCurlW_Dispose(curl);
CkJsonObjectW_Dispose(state);
CkFileAccessW_Dispose(files);
CkJsonObjectW_Dispose(page);
CkStringBuilderW_Dispose(seenPath);
CkStringBuilderW_Dispose(authorizedCommand1);
return;
}
more = (count > 0) && (nextPosition != CkHttpCurlW_getVar(curl,L"position"));
CkJsonObjectW_UpdateString(state,L"position",nextPosition);
success = CkJsonObjectW_WriteFile(state,statePath);
if (success != TRUE) {
wprintf(L"%s\n",CkJsonObjectW_lastErrorText(state));
CkHttpCurlW_Dispose(curl);
CkJsonObjectW_Dispose(state);
CkFileAccessW_Dispose(files);
CkJsonObjectW_Dispose(page);
CkStringBuilderW_Dispose(seenPath);
CkStringBuilderW_Dispose(authorizedCommand1);
return;
}
CkHttpCurlW_SetVar(curl,L"position",nextPosition);
batch = batch + 1;
}
wprintf(L"Checkpoint saved. Run again later to continue polling.\n");
CkHttpCurlW_Dispose(curl);
CkJsonObjectW_Dispose(state);
CkFileAccessW_Dispose(files);
CkJsonObjectW_Dispose(page);
CkStringBuilderW_Dispose(seenPath);
CkStringBuilderW_Dispose(authorizedCommand1);
}