PureBasic Requires Chilkat v11.5.0+
PureBasic
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 PureBasic Downloads
IncludeFile "CkFileAccess.pb"
IncludeFile "CkStringBuilder.pb"
IncludeFile "CkJsonObject.pb"
IncludeFile "CkHttpCurl.pb"
Procedure ChilkatExample()
success.i = 0
; Requires Chilkat 11.5.0 or later and a previously unlocked Chilkat bundle.
; See Global Unlock Sample.
; Guide: Box API Guide.
success = 0
; 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.i = CkHttpCurl::ckCreate()
If curl.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkHttpCurl::ckSetVar(curl,"access_token","BOX_ACCESS_TOKEN")
state.i = CkJsonObject::ckCreate()
If state.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
files.i = CkFileAccess::ckCreate()
If files.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
statePath.s = "qa_data/box_events_state.json"
If CkFileAccess::ckFileExists(files,statePath) = 1
success = CkJsonObject::ckLoadFile(state,statePath)
If success <> 1
Debug CkJsonObject::ckLastErrorText(state)
CkHttpCurl::ckDispose(curl)
CkJsonObject::ckDispose(state)
CkFileAccess::ckDispose(files)
ProcedureReturn
EndIf
If CkJsonObject::ckHasMember(state,"position") <> 1
Debug "Invalid checkpoint: position missing."
CkHttpCurl::ckDispose(curl)
CkJsonObject::ckDispose(state)
CkFileAccess::ckDispose(files)
ProcedureReturn
EndIf
Else
; "now" starts a new subscription. It deliberately skips historical events.
CkJsonObject::ckUpdateString(state,"position","now")
EndIf
CkHttpCurl::ckSetVar(curl,"position",CkJsonObject::ckStringOf(state,"position"))
page.i = CkJsonObject::ckCreate()
If page.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
seenPath.i = CkStringBuilder::ckCreate()
If seenPath.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
eventId.s
nextPosition.s
batch.i = 0
i.i
count.i
more.i = 1
While (more = 1) AND (batch < 20)
CkHttpCurl::ckClearTargetOutput(curl,"*")
authorizedCommand1.i = CkStringBuilder::ckCreate()
If authorizedCommand1.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkStringBuilder::ckSetString(authorizedCommand1,"curl -X GET " + Chr(34) + "https://api.box.com/2.0/events?stream_type=all&stream_position={{position}}&limit=100" + Chr(34) + " -H " + Chr(34) + "Authorization: Bearer {{access_token}}" + Chr(34))
headerReplacement1.i = CkStringBuilder::ckReplace(authorizedCommand1,"{{access_token}}",CkHttpCurl::ckGetVar(curl,"access_token"))
success = CkHttpCurl::ckDoYourThing(curl,CkStringBuilder::ckGetAsString(authorizedCommand1))
If (success <> 1) OR (CkHttpCurl::ckStatusCode(curl) <> 200)
Debug "Box request failed. HTTP status: " + Str(CkHttpCurl::ckStatusCode(curl))
Debug "Failure reason: " + Str(CkHttpCurl::ckFailReason(curl))
Debug CkHttpCurl::ckResponseBodyStr(curl)
CkHttpCurl::ckDispose(curl)
CkJsonObject::ckDispose(state)
CkFileAccess::ckDispose(files)
CkJsonObject::ckDispose(page)
CkStringBuilder::ckDispose(seenPath)
CkStringBuilder::ckDispose(authorizedCommand1)
ProcedureReturn
EndIf
success = CkHttpCurl::ckGetResponseJson(curl,page)
If success <> 1
Debug CkHttpCurl::ckLastErrorText(curl)
CkHttpCurl::ckDispose(curl)
CkJsonObject::ckDispose(state)
CkFileAccess::ckDispose(files)
CkJsonObject::ckDispose(page)
CkStringBuilder::ckDispose(seenPath)
CkStringBuilder::ckDispose(authorizedCommand1)
ProcedureReturn
EndIf
count = CkJsonObject::ckSizeOfArray(page,"entries")
i = 0
While i < count
CkJsonObject::setCkI(page, i)
eventId = CkJsonObject::ckStringOf(page,"entries[i].event_id")
If eventId = ""
Debug "Event without an event_id; checkpoint not advanced."
CkHttpCurl::ckDispose(curl)
CkJsonObject::ckDispose(state)
CkFileAccess::ckDispose(files)
CkJsonObject::ckDispose(page)
CkStringBuilder::ckDispose(seenPath)
CkStringBuilder::ckDispose(authorizedCommand1)
ProcedureReturn
EndIf
CkStringBuilder::ckClear(seenPath)
CkStringBuilder::ckAppend(seenPath,"seen.")
CkStringBuilder::ckAppend(seenPath,eventId)
If CkJsonObject::ckHasMember(state,CkStringBuilder::ckGetAsString(seenPath)) = 0
; Replace this logging with your idempotent business operation.
; Record success only after that operation succeeds.
Debug eventId + " " + CkJsonObject::ckStringOf(page,"entries[i].event_type") + " " + CkJsonObject::ckStringOf(page,"entries[i].source.id")
CkJsonObject::ckUpdateBool(state,CkStringBuilder::ckGetAsString(seenPath),1)
EndIf
i = i + 1
Wend
nextPosition = CkJsonObject::ckStringOf(page,"next_stream_position")
If nextPosition = ""
Debug "Missing next_stream_position; checkpoint not advanced."
CkHttpCurl::ckDispose(curl)
CkJsonObject::ckDispose(state)
CkFileAccess::ckDispose(files)
CkJsonObject::ckDispose(page)
CkStringBuilder::ckDispose(seenPath)
CkStringBuilder::ckDispose(authorizedCommand1)
ProcedureReturn
EndIf
more = (count > 0) AND (nextPosition <> CkHttpCurl::ckGetVar(curl,"position"))
CkJsonObject::ckUpdateString(state,"position",nextPosition)
success = CkJsonObject::ckWriteFile(state,statePath)
If success <> 1
Debug CkJsonObject::ckLastErrorText(state)
CkHttpCurl::ckDispose(curl)
CkJsonObject::ckDispose(state)
CkFileAccess::ckDispose(files)
CkJsonObject::ckDispose(page)
CkStringBuilder::ckDispose(seenPath)
CkStringBuilder::ckDispose(authorizedCommand1)
ProcedureReturn
EndIf
CkHttpCurl::ckSetVar(curl,"position",nextPosition)
batch = batch + 1
Wend
Debug "Checkpoint saved. Run again later to continue polling."
CkHttpCurl::ckDispose(curl)
CkJsonObject::ckDispose(state)
CkFileAccess::ckDispose(files)
CkJsonObject::ckDispose(page)
CkStringBuilder::ckDispose(seenPath)
CkStringBuilder::ckDispose(authorizedCommand1)
ProcedureReturn
EndProcedure