Sample code for 30+ languages & platforms
Xbase++ Requires Chilkat v11.5.0+

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 Xbase++ Downloads

Xbase++
LOCAL nSuccess
LOCAL oCurl
LOCAL oState
LOCAL oFiles
LOCAL cStatePath
LOCAL oPage
LOCAL oSeenPath
LOCAL cEventId
LOCAL cNextPosition
LOCAL nBatch
LOCAL i
LOCAL nCount
LOCAL nMore
LOCAL oAuthorizedCommand1
LOCAL nHeaderReplacement1

nSuccess := 0

//  Requires Chilkat 11.5.0 or later and a previously unlocked Chilkat bundle.
//  See Global Unlock Sample.
//  Guide: Box API Guide.

nSuccess := 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.
oCurl := CreateObject("Chilkat.HttpCurl")
oCurl:SetVar("access_token", "BOX_ACCESS_TOKEN")

oState := CreateObject("Chilkat.JsonObject")
oFiles := CreateObject("Chilkat.FileAccess")
cStatePath := "qa_data/box_events_state.json"
IF (oFiles:FileExists(cStatePath) == 1)
    nSuccess := oState:LoadFile(cStatePath)
    IF (nSuccess != 1)
        ? oState:LastErrorText
        oCurl:destroy()
        oState:destroy()
        oFiles:destroy()
        RETURN
    ENDIF

    IF (oState:HasMember("position") != 1)
        ? "Invalid checkpoint: position missing."
        oCurl:destroy()
        oState:destroy()
        oFiles:destroy()
        RETURN
    ENDIF

ELSE
    //  "now" starts a new subscription. It deliberately skips historical events.
    oState:UpdateString("position", "now")
ENDIF

oCurl:SetVar("position", oState:StringOf("position"))
oPage := CreateObject("Chilkat.JsonObject")
oSeenPath := CreateObject("Chilkat.StringBuilder")

nBatch := 0

nMore := 1
DO WHILE (nMore == 1) .AND. (nBatch < 20)
    oCurl:ClearTargetOutput("*")
    oAuthorizedCommand1 := CreateObject("Chilkat.StringBuilder")
    oAuthorizedCommand1:SetString('curl -X GET "https://api.box.com/2.0/events?stream_type=all&stream_position={{position}}&limit=100" -H "Authorization: Bearer {{access_token}}"')
    nHeaderReplacement1 := oAuthorizedCommand1:Replace("{{access_token}}", oCurl:GetVar("access_token"))
    nSuccess := oCurl:DoYourThing(oAuthorizedCommand1:GetAsString())
    IF ((nSuccess != 1) .OR. (oCurl:StatusCode != 200))
        ? "Box request failed. HTTP status: " + Str(oCurl:StatusCode)
        ? "Failure reason: " + Str(oCurl:FailReason)
        ? oCurl:ResponseBodyStr
        oCurl:destroy()
        oState:destroy()
        oFiles:destroy()
        oPage:destroy()
        oSeenPath:destroy()
        oAuthorizedCommand1:destroy()
        RETURN
    ENDIF

    nSuccess := oCurl:GetResponseJson(oPage)
    IF (nSuccess != 1)
        ? oCurl:LastErrorText
        oCurl:destroy()
        oState:destroy()
        oFiles:destroy()
        oPage:destroy()
        oSeenPath:destroy()
        oAuthorizedCommand1:destroy()
        RETURN
    ENDIF

    nCount := oPage:SizeOfArray("entries")
    i := 0
    DO WHILE i < nCount
        oPage:I := i
        cEventId := oPage:StringOf("entries[i].event_id")
        IF (cEventId == "")
            ? "Event without an event_id; checkpoint not advanced."
            oCurl:destroy()
            oState:destroy()
            oFiles:destroy()
            oPage:destroy()
            oSeenPath:destroy()
            oAuthorizedCommand1:destroy()
            RETURN
        ENDIF

        oSeenPath:Clear()
        oSeenPath:Append("seen.")
        oSeenPath:Append(cEventId)
        IF (oState:HasMember(oSeenPath:GetAsString()) == 0)
            //  Replace this logging with your idempotent business operation.
            //  Record success only after that operation succeeds.
            ? cEventId + " " + oPage:StringOf("entries[i].event_type") + " " + oPage:StringOf("entries[i].source.id")
            oState:UpdateBool(oSeenPath:GetAsString(), 1)
        ENDIF

        i := i + 1
    ENDDO
    cNextPosition := oPage:StringOf("next_stream_position")
    IF (cNextPosition == "")
        ? "Missing next_stream_position; checkpoint not advanced."
        oCurl:destroy()
        oState:destroy()
        oFiles:destroy()
        oPage:destroy()
        oSeenPath:destroy()
        oAuthorizedCommand1:destroy()
        RETURN
    ENDIF

    nMore := (nCount > 0) .AND. (cNextPosition != oCurl:GetVar("position"))
    oState:UpdateString("position", cNextPosition)
    nSuccess := oState:WriteFile(cStatePath)
    IF (nSuccess != 1)
        ? oState:LastErrorText
        oCurl:destroy()
        oState:destroy()
        oFiles:destroy()
        oPage:destroy()
        oSeenPath:destroy()
        oAuthorizedCommand1:destroy()
        RETURN
    ENDIF

    oCurl:SetVar("position", cNextPosition)
    nBatch := nBatch + 1
ENDDO
? "Checkpoint saved. Run again later to continue polling."

oCurl:destroy()
oState:destroy()
oFiles:destroy()
oPage:destroy()
oSeenPath:destroy()
oAuthorizedCommand1:destroy()