I'm just learning to use Web Tools - want to be able to connect to REST APIs. The first thing I tried is a very basic example that was found in the "samples" provided in the help docs. Each time running it (and other code as well) an error that says “Variable ‘WWWVERSION_NUMBER’ is not found”.
Here is the code:
SET PATH TO "F:\Westwind\wwClient-7.29.0\classes" ADDITIVE
SET EXACT OFF
do wwHttp
loProxy = CREATEOBJECT("wwJsonServiceClient")
lcUrl = "https://albumviewer.west-wind.com/api/artists"
loArtists = loProxy.CallService(lcUrl)
IF (loProxy.lError OR ISNULL(loArtists))
? loProxy.cErrorMsg
RETURN
ENDIF
*** Service returns an array which is turned into a FoxPro collection
? loArtists.Count && Collection
FOR EACH loArtist in loArtists
? loArtist.ArtistName
? loArtist.Description
? loArtist.ImageUrl
? "---"
ENDFOR
I think its something to do with missing files or dependencies not being set up properly but all the files seem to be available.
thanks.

You must make sure that the file wconnect.h is in your path.
Thanks. I thought of that, and the file is in the same directory as the program I am trying and also in the "classes" subdirectory, which I reference with the PATH statement.
Do I need to reference wconnect.h somewhere in the code?
I just got it working by compiling wwhttp.prg. Ran it again and it worked!
Thanks for your help. I appreciate it.
You need to add the path to wconnect.h
which is not in the classes
folder but the one above it (or copy the file somewhere where FoxPro can find it).
SET PATH TO "F:\Westwind\wwClient-7.29.0" ADDITIVE
SET PATH TO "F:\Westwind\wwClient-7.29.0\classes" ADDITIVE
+++ Rick ---

The main reason I purchased the tools was to be able to connect to the Monday.com API, among others. After multiple hours of 'trial and error, I was able to connect and make a post successfully, but it says that the body of the post is malformed. Monday.com actually uses 'GraphQL', as explained in this link. When I post the body into their 'playground', it works but does not work with my post. After trying to form the body organically with the tools, I decided to post the code verbatim right in the JSON string to see if it would work.
Here is my code:
loProxy = CREATEOBJECT("wwJsonServiceClient")
DO wwJsonSerializer
DO wwDynamic
loCust = CREATEOBJECT("wwDynamic")
lcJson = 'query {boards(limit:1) {id name}}'
? lcJson
*** Create custom Http Object for Authentication
loHttp = CREATEOBJECT("wwHttp")
loHttp.AddHeader("Content-Type","application/json")
loHttp.AddHeader("Authorization","TOKENGOESHERE")
*** Pass it to the proxy to use
loProxy.CreatewwHttp(loHttp)
loCust = loProxy.CallService("https://api.monday.com/v2",lcJson,"POST")
IF loProxy.lError
? loProxy.cErrorMsg
RETURN
ENDIF
oResult = JsonSerialize(loCust)
? oResult
I'm sure there's a question in that message somewhere... 😄
GraphQL isn't JSON but rather it's a raw GraphQL query string. You're trying to convert the raw string to JSON which is going to break the value.
Short answer is you can't use wwJsonService
for a GraphQL query, because wwJsonService expects any input to be a JSON value, not a raw string. For this to work you need to use raw wwHTTP
and manually deserialize the JSON that comes back.
+++ Rick ---
You are right - I did not ask a specific question - my bad. I did say that the body was malformed and assumed you knew I needed to know why. Do you have an example of how to do "raw wwHTTP"? Is there a function within your Web Tools to do that?
I've referred you to the REST API article, which has all of this information? The documentation also has examples of this exact scenario.
- Building and Consuming REST API Services with FoxPro
- Access HTTP content over the Web (Making REST API Calls)
+++ Rick ---
Rick, sorry to be a pain, but I've spent hours on the 'basic' documents you sent me and I still cannot get it to work. I think their version of GraphQL may be a little different. Seems that in the Javascript example they give, they stringify the body, which tells me it is more than just a raw string of data. My question is this: should I 'serialize' (using your terminology) the body text before sending and if so, how do I indicate that this goes in the 'body' of the string I send to them? Here is a screenshot of their help document:
Thanks again.
It's a raw string. They are stringifying an object. You're going to provide the raw JSON string.
You can stringify this:
loQuery = CREATEOJBECT("EMPTY")
ADDPROPERTY(loQuery, "query", ['{ boards(limit: 1) { id:name } }'])
loResult = loService.CallMethod(lcUrl, loQuery)
My point is that creating the JSON and then embedding the GraphQL query as a string is often a waste of time, because the query itself is a string not an object. I suppose once you add parameters using an object makes more sense as you can dynamically provide the dynamic values needed to run the query.
or manually:
loQuery = CREATEOJBECT("EMPTY")
ADDPROPERTY(loQuery, "query", ['{ boards(limit: 1) { id:name } }'])
loSer = CREATEOBJECT("wwJsonSerializer")
lcJson = loSer.Serialize(loQuery)
loHttp = CREATEOBJECT("wwHttp")
loHttp.cContentType = "application/json"
lcJsonResult = loHttp.Post(lcUrl, STRCONV(lcJson,9))
Based on what I saw in the documentation why are you using the GraphQL anyway - they have a JSON API that will be easier to use from the FoxPro end.
+++ Rick ---
Thank You, Rick! It's working much better; the only thing it's complaining now about is the double quotation marks. If I take the double quotation marks out of the string and post it into the playground, it loves it! I tried to strip them out with strtran() but then the call didn't work at all. Any ideas on the quotation marks issue? Where did you see there is another API available besides GraphQL? I didn't know I had a choice. Thanks again; your software is awesome. Here is what I have now:
propname='query'
loQuery = CREATEOBJECT("EMPTY")
ADDPROPERTY(loQuery, propname, [boards(limit: 1) { id:name } ])
Nah that's just me reading the GraphQL incorrectly. The inner quotes were wrong - that's just the JSON representation of the GQL query string.
+++ Rick ---
Thanks, Rick. As you can see in the code I posted, I already took those inner quotes out. It seems like it adds double quotes on its own for some reason. Any suggestions?
interesting information
Quotes are the string delineator.
+++ Rick ---
Here is the code I use:
loSer = CREATEOBJECT("wwJsonSerializer")
lcJson = loSer.Serialize(loQuery)
viewstring=STRTOFILE(lcJson,"f:\westwind\json_string.txt")
Here is what is in the file(json_string.txt) {"query": "boards(limit: 1) { id:name }"}
If I take those quotes out and paste it into the 'playground', it loves it. Posting it using the vfp program produces a parse error.
Is there a way to take the quotes out? Thanks.
without the quotes it's not valid JSON.
GraphQL queries - the query string anyway, are strings. you have to format the string appropriately as a string.
See here:
Make GraphQL requests from Curl
Same thing there. The query is a string not an object.
+++ Rick ---
Sorry to bother you again. I reached out to Monday.com and told them my dilemma about not being able to access their API because my script produces valid JSON. I also asked them if they had a REST API to use instead and here was their response:
No we do not use REST API and our API is based on GraphQL, so you should use the GraphQL format as stated in our documentation.
What needs to be in JSON format is your column values if you are looking to mutate data. The returned data is also in JSON format.
Why are you trying to change your query into JSON? This wouldn't work in any setting as the query needs to follow the GraphQL format as stated in our documentation.
I am really in a tough spot now. I saw on your website that you do consultations in addition to selling software. Can I hire you to help me figure this out?
Sure I can take a look. Send me a private email and we can go from there...
+++ Rick ---
OK, well I scrapped Monday.com because I couldn't connect to their API from Westwind. I invested in a new CSR: KEAP, which has a regular REST API. Its complaining because it is looking for some brackets in the JSON string. Here is what it is expecting (from the help documentation):
--data-raw '{
"email_addresses":[
{
"email":"my_first_api@example.com",
"field":"EMAIL1",
"opt_in_reason":"Opt-in reason"
}
]
}'
Here is what my string looks like: {"email_addresses": {"email": "gbarfield@benebloc.com","field": "EMAIL1","opt_in_reason": "Opt-in reason"}}
If I put those 'brackets' "[" "]" in as their example has, I can paste it into Postman and it works fine.
Here is my VFP code:
SET PATH TO "F:\Westwind\wwClient-7.29.0" ADDITIVE
SET PATH TO "F:\Westwind\wwClient-7.29.0\classes" ADDITIVE
SET EXACT OFF
DO wwHttp
loObject = CREATEOBJECT("EMPTY")
DO wwJsonSerializer
loSer = CREATEOBJECT("wwJsonSerializer")
lcJson = loSer.Serialize(loObject)
loCustomer = CREATEOBJECT("EMPTY")
ADDPROPERTY(loCustomer, "email_addresses")
*** Add a nested object
loAddress = CREATEOBJECT("EMPTY")
ADDPROPERTY(loCustomer, "email_addresses", loAddress) && Add as child to ^customer
ADDPROPERTY(loAddress, "email", "gregg@test.com")
ADDPROPERTY(loAddress, "field", "EMAIL1")
ADDPROPERTY(loAddress, "opt_in_reason", "Opt-in reason")
loSer = CREATEOBJECT("wwJsonSerializer")
lcJson = loSer.Serialize(loCustomer)
STRTOFILE(lcJson,"f:\westwind\jsonfile.txt")
loHttp = CREATEOBJECT("wwHttp")
loHttp.AddHeader("Content-Type","application/json")
loHttp.AddHeader("X-Keap-API-Key","mytokengoeshere")
lcJsonResult = loHttp.Post("https://api.infusionsoft.com/crm/rest/v2/contacts?fields=email_addresses", lcJson)
? lcJsonResult
What I am doing wrong?
email_addresses
is an array in the JSON. To create that from FoxPro with wwJsonSerializer
you need to use a Collection
object, then add the item to the collection for the JSON to render the way you have it there.
+++ Rick ---
Thank you, Rick. I am making progress. Now I just need to put 'email_addresses' as the name of the collection (outside the brackets). I think I need to add 'child' to loCustomer. Can you show me how to do that to make it come out correctly. I spent a couple of hours trying. Here is my code:
DO wwHttp
loObject = CREATEOBJECT("EMPTY")
DO wwJsonSerializer
loSer = CREATEOBJECT("wwJsonSerializer")
lcJson = loSer.Serialize(loObject)
loCol = CREATEOBJECT("Collection")
loCustomer = CREATEOBJECT("EMPTY")
ADDPROPERTY(loCustomer,"email_addresses")
loAddress = CREATEOBJECT("EMPTY")
ADDPROPERTY(loAddress, "email", "gregg@test.com")
ADDPROPERTY(loAddress, "field", "EMAIL1")
ADDPROPERTY(loAddress, "opt_in_reason", "Opt-in reason")
loCol.Add(loAddress)
lcjson = loSer.Serialize(loCol, .T.)
Thank you in advance.
You have to match the object structure. Top level is an object that contains the collection:
*** Top level object (no name)
loEntity = CREATEOBJECT("EMPTY")
ADDPROPERTY(loEntity, "email_addresses")
*** Create the email item to add to collection
loEmail = CREATEOBJECT("EMPTY")
ADDPROPERTY(loEmail,"email","blah@blah.com")
* ...
*** Create the collection and add Email
loCol = CREATEOBJECT("Collection")
loCol.Add(loEmail)
*** Set the collection to the object Emails
loEntity.email_addresses = loCol
*** serializer
lcJson = loSer.Serialize(loEntity)
It works! You rock. Thanks!
So, the example you sent me to add a collection works great; thanks! Now I need to 'stack' these collections along with other segments in the payload. I have tried but always seem to 'overwrite' the last one. This diagram depicts what it needs to look like. I assume the segments with the brackets are the 'collections'. How do I put them all together?
Look Greg, you're going to have to do some of this work yourself. I'm not here to do your work for you... I've given you everything you need to know how to do this.
There's no such thing as stacking. It's just additional properties.
+++ Rick ---