Last active
December 29, 2015 08:29
-
-
Save brucemcpherson/7644016 to your computer and use it in GitHub Desktop.
VBA API for parse.com
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Option Explicit | |
' a VBA class for parse.com | |
' v1.2 | |
Private pBrowser As cBrowser | |
Private pPackage As cJobject | |
Private pClass As String | |
Private pApplicationHeaders As cJobject | |
Private pSalt As String | |
Private pBatch As cJobject | |
Private pBatchPoint As String | |
Private pBatchMode As Boolean | |
Private pEndPoint As String | |
Private pClassPoint As String | |
Private pBatchMax As Long | |
Public Property Get parseClass() As String | |
parseClass = pClass | |
End Property | |
Public Property Let parseClass(p As String) | |
pClass = p | |
End Property | |
Public Function resultsLength(Optional job As cJobject = Nothing) As Long | |
Dim j As cJobject | |
Set j = job | |
If j Is Nothing Then | |
Set j = jObject | |
End If | |
resultsLength = j.child("results").children.count | |
If job Is Nothing Then | |
j.tearDown | |
End If | |
End Function | |
Public Function count(Optional queryJob As cJobject = Nothing, Optional queryParams As cJobject = Nothing) As Long | |
count = 0 | |
With getCount(queryJob, queryParams) | |
If .isOk Then | |
With .jObject | |
count = .child("count").value | |
.tearDown | |
End With | |
End If | |
End With | |
End Function | |
Public Property Get self() As cParseCom | |
Set self = Me | |
End Property | |
Public Property Get jObject() | |
Set jObject = JSONParse(browser.Text) | |
End Property | |
Public Property Get browser() | |
Set browser = pBrowser | |
End Property | |
Public Property Get isOk() As Boolean | |
isOk = False | |
If pBrowser.isOk Or (pBrowser.status = 0 And pBatchMode) Then | |
If Not pBatchMode Then | |
isOk = True | |
Else | |
' need to check for errors in all the flushed batch | |
With jObject | |
isOk = .find("error") Is Nothing | |
.tearDown | |
End With | |
End If | |
End If | |
End Property | |
Public Function init(whichClass As String, _ | |
Optional credentialsEntry As String = "parse", _ | |
Optional scopeEntry As String = "rest", _ | |
Optional restAPIKey As String = vbNullString, _ | |
Optional clientKey As String = vbNullString) As cParseCom | |
Set pPackage = getParseCredentials(credentialsEntry, scopeEntry, restAPIKey, clientKey) | |
If pPackage Is Nothing Then | |
Exit Function | |
End If | |
Set pApplicationHeaders = getApplicationHeaders | |
pClass = whichClass | |
Set init = Me | |
End Function | |
Public Function getObjectById(id As String) As cParseCom | |
Set getObjectById = getStuff("/" & id) | |
End Function | |
Public Function getObjectsByQuery(Optional queryJob As cJobject = Nothing, _ | |
Optional queryParams As cJobject = Nothing) As cParseCom | |
Set getObjectsByQuery = getStuff(vbNullString, constructQueryString(queryJob, queryParams)) | |
End Function | |
Private Function constructQueryString(Optional queryJob As cJobject = Nothing, _ | |
Optional queryParams As cJobject = Nothing) As String | |
Dim qString As String, t As cStringChunker, job As cJobject | |
' set up parameters | |
Set t = New cStringChunker | |
If Not queryParams Is Nothing Then | |
For Each job In queryParams.children | |
t.add(job.key).add("=").add(job.toString).add ("&") | |
Next job | |
End If | |
' set up query string | |
If Not queryJob Is Nothing Then | |
t.add URLEncode("where=" & JSONStringify(queryJob)) | |
End If | |
qString = vbNullString | |
t.chopIf "&" | |
If t.size > 0 Then | |
qString = "?" & t.content | |
End If | |
Set t = Nothing | |
constructQueryString = qString | |
End Function | |
Private Function mergeParameters(Optional queryParams As cJobject = Nothing, Optional addParams As cJobject = Nothing) As cJobject | |
Dim job As cJobject | |
' start with the given params | |
If Not queryParams Is Nothing Then | |
' take a copy | |
Set job = JSONParse(queryParams.stringify) | |
End If | |
' add some more | |
If Not addParams Is Nothing Then | |
If job Is Nothing Then | |
Set job = New cJobject | |
job.init Nothing | |
End If | |
job.merge addParams | |
End If | |
Set mergeParameters = job | |
End Function | |
Public Function getCount(Optional queryJob As cJobject = Nothing, Optional queryParams As cJobject = Nothing) As cParseCom | |
Set getCount = getStuff(vbNullString, _ | |
constructQueryString(queryJob, mergeParameters(queryParams, JSONParse("{'count':1,'limit':0}")))) | |
End Function | |
Public Function createObject(addJob As cJobject) As cParseCom | |
Set createObject = postStuff(vbNullString, addJob) | |
End Function | |
Public Function updateObjects(Optional queryJob As cJobject = Nothing, _ | |
Optional updateJob As cJobject = Nothing, Optional queryParameters As cJobject = Nothing) As cParseCom | |
' does a query, then update all matching | |
Dim queryResponse As cJobject, skip As Long, jobSkip As cJobject, number As Long | |
skip = 0 | |
Set jobSkip = JSONParse("{'skip':0}") | |
' we'll just use the default limit | |
Do | |
With getObjectsByQuery(queryJob, mergeParameters(queryParameters, jobSkip)).jObject | |
' this is how many were returned this time | |
number = resultsLength(.self) | |
' if there were any then do soemthing with it | |
If number > 0 Then | |
' skip what we've already had | |
skip = skip + number | |
jobSkip.child("skip").value = skip | |
' update the contents of the query we just did | |
updateObjectsPart .self, updateJob | |
End If | |
' clear out these results | |
.tearDown | |
End With | |
' if there were any errors or there's no more to do then exit | |
Loop While isOk And number > 0 | |
Set updateObjects = Me | |
End Function | |
Private Function updateObjectsPart(queryResponse As cJobject, updateJob As cJobject) As cParseCom | |
Dim job As cJobject | |
' any matching get the same update | |
If isOk And Not queryResponse Is Nothing Then | |
With queryResponse | |
For Each job In .child("results").children | |
postStuff job.child("objectId").value, updateJob, "PUT" | |
Next job | |
.tearDown | |
End With | |
End If | |
Set updateObjectsPart = Me | |
End Function | |
Public Function deleteObjects(Optional queryJob As cJobject = Nothing) As cParseCom | |
Dim queryResponse As cJobject | |
' query is limited, so we need to keep going until no results | |
While self.count > 0 | |
Set queryResponse = getObjectsByQuery(queryJob).jObject | |
deleteObjectsPart queryResponse | |
If Not isOk Then | |
MsgBox "failed to flush:" & browser.status & ":" & browser.Text | |
Exit Function | |
End If | |
queryResponse.tearDown | |
Wend | |
Set deleteObjects = Me | |
End Function | |
Private Function deleteObjectsPart(queryResponse As cJobject) As cParseCom | |
' does a query, then update all matching | |
Dim job As cJobject | |
' any matching get deleted | |
If isOk Then | |
For Each job In queryResponse.child("results").children | |
deleteObject job.child("objectId").value | |
Next job | |
End If | |
Set deleteObjectsPart = Me | |
End Function | |
Public Function deleteObject(id As String) As cParseCom | |
If pBatchMode Then | |
postStuff id, , "DELETE" | |
Else | |
Set deleteObject = getStuff("/" & id, , "DELETE") | |
End If | |
End Function | |
Public Function postStuff(what As String, Optional data As cJobject = Nothing, _ | |
Optional method As String = "POST") As cParseCom | |
If pBatchMode Then | |
If isEmptyBatchNeeded Then flush | |
addToBatch method, pClassPoint & parseClass & "/" & what, data | |
Else | |
doPost pEndPoint & pClassPoint & parseClass & "/" & what, data, method | |
End If | |
Set postStuff = Me | |
End Function | |
Public Function getStuff(what As String, Optional params As String = vbNullString, Optional method As String = "GET") As cParseCom | |
Dim post As String | |
'always need to flush before a get | |
flush | |
pBrowser.httpGET pEndPoint & pClassPoint & parseClass & what & params, , , , , , pApplicationHeaders, method | |
Set getStuff = Me | |
End Function | |
Private Function doPost(url As String, Optional data As cJobject = Nothing, Optional method As String = "POST") As cParseCom | |
' called when we need to issue a get | |
Dim dString As String | |
If Not data Is Nothing Then dString = data.stringify | |
pBrowser.httpPost url, dString, True, , pApplicationHeaders, method | |
Set doPost = Me | |
End Function | |
Private Function clearDown(o As Object) As cParseCom | |
If Not o Is Nothing Then | |
o.tearDown | |
Set o = Nothing | |
End If | |
Set clearDown = Me | |
End Function | |
Private Function isEmptyBatchNeeded() As Boolean | |
' there's a maximum to how many we can batch at once | |
isEmptyBatchNeeded = False | |
If Not pBatch Is Nothing Then isEmptyBatchNeeded = (pBatch.child("requests").children.count >= pBatchMax) | |
End Function | |
Private Function addToBatch(method As String, path As String, Optional body As cJobject = Nothing) | |
If pBatch Is Nothing Then | |
Set pBatch = New cJobject | |
pBatch.init Nothing | |
End If | |
' first in? | |
If Not pBatch.hasChildren Then | |
pBatch.add("requests").addArray | |
End If | |
With pBatch.child("requests").add | |
If Right(path, 1) = "/" Then path = left(path, Len(path) - 1) | |
.add "method", method | |
.add "path", path | |
If Not body Is Nothing Then | |
With .add("body") | |
.append body | |
End With | |
End If | |
End With | |
Set addToBatch = Me | |
End Function | |
Public Function batch(Optional batchItUp As Boolean = True) As cParseCom | |
' use this to set up batching. if any outstanding it will clear it if changing batching mode | |
If Not batchItUp Then | |
flush | |
End If | |
pBatchMode = batchItUp | |
Set batch = Me | |
End Function | |
Public Property Get batchMode() As Boolean | |
batchMode = pBatchMode | |
End Property | |
Public Function flush() | |
' been storing stuff up | |
If Not pBatch Is Nothing Then | |
If (pBatch.hasChildren) Then | |
doPost pEndPoint & pBatchPoint, pBatch, "POST" | |
If Not isOk Then | |
MsgBox "failed to flush:" & browser.status & ":" & browser.Text | |
End If | |
pBatch.tearDown | |
End If | |
Set pBatch = Nothing | |
End If | |
Set flush = Me | |
End Function | |
Public Sub tearDown() | |
clearDown pBrowser | |
clearDown pPackage | |
clearDown pApplicationHeaders | |
clearDown pBatch | |
End Sub | |
Private Sub Class_Initialize() | |
Set pBrowser = New cBrowser | |
pEndPoint = "https://api.parse.com" | |
pClassPoint = "/1/classes/" | |
pBatchPoint = "/1/batch" | |
pSalt = "xLiberation" | |
pBatchMode = False | |
pBatchMax = 50 | |
End Sub | |
Private Function getParseCredentials(entry As String, scope As String, _ | |
Optional restAPIKey As String = vbNullString, _ | |
Optional clientKey As String = vbNullString) As cJobject | |
Set pPackage = getRegistryPackage(entry, scope) | |
If pPackage Is Nothing Then | |
If (restAPIKey = vbNullString Or clientKey = vbNullString) Then | |
MsgBox ("First time you need to provide keys") | |
Exit Function | |
End If | |
Set pPackage = New cJobject | |
With pPackage.init(Nothing) | |
.add "scopeEntry", scope | |
.add "authFlavor", entry | |
.add "restAPIKey", restAPIKey | |
.add "applicationID", clientKey | |
End With | |
setRegistryPackage | |
End If | |
Set getParseCredentials = pPackage | |
End Function | |
Private Function getApplicationHeaders() As cJobject | |
Dim job As cJobject, a As cJobject | |
Set job = New cJobject | |
With job.init(Nothing) | |
.add "X-Parse-Application-Id", pPackage.child("applicationID").value | |
.add "X-Parse-REST-API-Key", pPackage.child("restAPIKey").value | |
End With | |
Set getApplicationHeaders = job | |
End Function | |
'" ---- registry ----- | |
'" in registry entries, the values are encrypted useing the salt | |
'" the structure is | |
'" xLiberation/parseAuth/scope - json pPackage values | |
Private Function getRegistryPackage(authFlavor As String, scopeEntry As String) As cJobject | |
Dim s As String | |
s = GetSetting("xLiberation", _ | |
authFlavor, _ | |
scopeEntry) | |
If (s <> vbNullString) Then Set getRegistryPackage = JSONParse(decrypt(s)) | |
End Function | |
Private Function setRegistryPackage() As cJobject | |
Dim s As String | |
s = JSONStringify(pPackage) | |
SaveSetting "xLiberation", _ | |
pPackage.child("authFlavor").value, _ | |
pPackage.child("scopeEntry").value, _ | |
encrypt(s) | |
End Function | |
Private Function encrypt(s As String) As String | |
' " uses capicom | |
encrypt = encryptMessage(s, pSalt) | |
End Function | |
Private Function decrypt(s As String) As String | |
' " uses capicom | |
decrypt = decryptMessage(s, pSalt) | |
End Function |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Option Explicit | |
'v1.2 | |
Private Sub firstTimeParseCom() | |
Dim parseCom As cParseCom | |
Set parseCom = New cParseCom | |
With parseCom.init("ColorTable", , , "your app id", _ | |
"your restapikey") | |
.tearDown | |
End With | |
End Sub | |
Private Sub populates() | |
' copy two sheets to parse.com | |
populateFromSheet "VBAParseCustomers" | |
populateFromSheet "VBAParseData" | |
End Sub | |
Private Sub populateFromSheet(sheetName As String) | |
Dim parseCom As cParseCom, job As cJobject, dset As cDataSet | |
' this will clear out an existing parse class, and create a new one from a worksheet | |
' we'll use batch mode throughout | |
Set parseCom = getParsed(sheetName).batch | |
' clear out existing any existing data | |
parseCom.deleteObjects | |
'get the data from the sheet and populate database | |
Set dset = New cDataSet | |
With dset.populateData(wholeSheet(sheetName), , , , , , True).jObject(, True, True) | |
For Each job In .children | |
With parseCom.createObject(job) | |
' clear this error handling up | |
Debug.Assert .isOk | |
End With | |
Next job | |
' clear up | |
.tearDown | |
End With | |
' commit any outstanding and clean up | |
With parseCom.flush.batch(False) | |
Debug.Assert .isOk | |
' show how many are there now | |
Debug.Print .count & " in class" & sheetName | |
.tearDown | |
End With | |
End Sub | |
Private Sub testGetItemByUniqueId() | |
' get an item by unique object ID | |
With getParsed("VBAParseCustomers") | |
.getObjectById ("SmnyjZKs9m") | |
' test if it worked, and do something with the results | |
If .isOk Then | |
Debug.Print .jObject.stringify(True) | |
Else | |
Debug.Print "failed to get object:" & .browser.url & ":" & .browser.status & ":" & .browser.Text | |
End If | |
.tearDown | |
End With | |
End Sub | |
Private Sub testparseUpdate() | |
' get some items by query and change the scheme name to something else | |
With getParsed("VBAParseData").batch | |
With .updateObjects(JSONParse("{'customerid':39}"), JSONParse("{'customerid':1}")) | |
' test if it worked, and do something with the results | |
If .isOk Then | |
Debug.Print "all is good", .jObject.stringify | |
Else | |
Debug.Print "failed to update:" & .browser.url & ":" & .browser.status & ":" & .browser.Text | |
End If | |
End With | |
.flush.tearDown | |
End With | |
End Sub | |
Private Sub testparsequery() | |
' get a number of items that match a query by example | |
With getParsed("VBAParseData") | |
With .getObjectsByQuery(JSONParse("{'customerid':1}"), JSONParse("{'limit':2}")) | |
'test if it worked, and do something with the results | |
If .isOk Then | |
Debug.Print "all is ok", .jObject.stringify(True) | |
Else | |
Debug.Print "failed to do query:" & .browser.url & ":" & .browser.status & ":" & .browser.Text | |
End If | |
End With | |
.tearDown | |
End With | |
End Sub | |
Private Sub testparseCount() | |
' get a number of items that match a query by example | |
Debug.Print getParsed("VBAParseData").count(JSONParse("{'customerid':1}")) | |
End Sub | |
Private Sub parseMatch() | |
Dim pCustomer As New cParseCom, pData As cParseCom, job As cJobject, joc As cJobject, queryJob As cJobject | |
' look up data in another table based on data in another | |
' data abot the pantone colors of the year | |
Set pCustomer = getParsed("VBAParseCustomers") | |
Set pData = getParsed("VBAParseData") | |
' set up a query by example, restricting to a particular customer | |
Set queryJob = New cJobject | |
queryJob.init(Nothing).add "country", "United States" | |
' go through all matching customers | |
With pCustomer.getObjectsByQuery(queryJob) | |
If .isOk Then | |
With .jObject.child("results") | |
For Each job In .children | |
With pData.getObjectsByQuery(job.child("customerid")) | |
If .isOk Then | |
With .jObject.child("results") | |
For Each joc In .children | |
Debug.Print job.toString("country"), job.toString("name"), job.child("customerid").value, joc.child("invoiceid").value | |
Next joc | |
End With | |
End If | |
End With | |
Next | |
End With | |
End If | |
End With | |
' clean up | |
queryJob.tearDown | |
pCustomer.tearDown | |
pData.tearDown | |
End Sub | |
Public Function getParsed(parseClass As String) As cParseCom | |
Dim p As cParseCom | |
Set p = New cParseCom | |
Set getParsed = p.init(parseClass) | |
End Function |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment