Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Loren Doornek

#1
Programming / Re: trying to use a web service (API)
September 09, 2025, 12:46:31 PM
With a GET command, you usually don't pass it any data since it is just GETting something. Passing data to the API usually uses a POST or PUT.

This simple call works for me:

CALL "*plus/web/request","https://cataas.com/api/count",$$,recvdata$
PRINT RECVDATA$

{"count":1987}
#2
Web Services / Re: Unexpected Response in an HTTP Request
September 04, 2025, 06:47:13 PM
The Content-Type is "text/xml" in the PXP version, but "application/xml" in the Postman version.  That may affect how the API host is interpreting the data.
#3
Jeff, I've run into the same thing.  You need PXPlus version 17 or higher to automatically build the JSON in this format.  Since we have some clients using older versions, I've done as you suggested - build the inner JSON, then add the beginning and ending square brackets. 

Here's code that works on version 17, which generates the JSON as you expect:

j$["1.Order"]="123456"
j$["1.Customer"]="ABC Co"
j$["2.Order"]="123457"
j$["2.Customer"]="XYZ Co"
PRINT DIM(LIST EDIT J$)


[
  {
    "Order":"123456",
    "Customer":"ABC Co"
  },
  {
    "Order":"123457",
    "Customer":"XYZ Co"
  }
]


Also be aware that any response they send back may be encapsulated in the square brackets.  When I see that, I just strip them off, or replace the initial square brackets with "{data:[" and append a "}", so that the JSON is in format that earlier versions of PXPlus can work with.  Below is an example of doing that, using the same data as above.

print j$
[{"Order":"123456","Customer":"ABC Co"},{"Order":"123457","Customer":"XYZ Co"}]

dim load json$=j$ ! gets an error since the JSON starts with [
Error #17: Invalid file type or contents

jj$=sub(j$,"[","{data:[")+"}" ! convert the JSON to a new variable JJ$
dim load json$=jj$
print dim(list edit json$)

{
  "data":[
    {
      "Order":"123456",
      "Customer":"ABC Co"
    },
    {
      "Order":"123457",
      "Customer":"XYZ Co"
    }
  ]
}
#4
Language / Re: Sort on Hdr Click
July 03, 2025, 02:51:14 PM
If your grid is named INPF_GRID, then you should use INPF_GRID.CTL'sort=5 (or -5 if you want to sort in reverse order).  You need to set the 'sort value AFTER data is loaded, and set it again if you reload the grid so that the newly-loaded data is sorted as expected. 

Whenever I need to reload a sorted item (grid or listbox), I will store the current sort that the user has selected using logic like "LET cur_sort=INPF_GRID.CTL'SORT". Then, after reloading the data in the grid/listbox, I'll set the sort back to whatever the user had previously selected using "LET INPF_GRID.CTL'SORT=cur_sort"
#5
Programming / Re: Errors Bubbling Up
March 26, 2025, 11:11:29 AM
Check the value of the 'NE' system parameter.  If off, it will report all errors to the ultimate parent program.
#6
Programming / Re: REST to GRAPHQL
February 26, 2025, 05:02:53 PM
And here is the resulting query string:

{"query":"mutation { productVariantsBulkUpdate(productId: \"gid://shopify/Product/4353068859479\", variants:[{id:\"gid://shopify/ProductVariant/31174618611799\",barcode:\"190496879522\"}]) { product { id } productVariants { id barcode } userErrors { field message } } }"}
#7
Programming / Re: REST to GRAPHQL
February 26, 2025, 05:01:12 PM
0010 BEGIN
0020 LET gid$="gid://shopify/Product/4353068859479"
0030 LET j$["variants.1.id"]="gid://shopify/ProductVariant/31174618611799",j$["variants.1.barcode"]="190496879522"
0040 ! LET j$["variants.2.id"]="gid://shopify/ProductVariant/31174618611799",j$["variants.2.barcode"]="190496879522" ! Additional variants to update
0050 LET v$=DIM(LIST j${ALL})
0060 DIM r$[1:3]; READ DATA FROM "variants,id,barcode,",SEP="," TO r${ALL}; FOR i=1 TO 3; LET v$=SUB(v$,QUO+r$[i]+QUO+":",r$[i]+":"); NEXT i; LET v$=MID(v$,2,LEN(v$)-2) ! Remove the quotes from around the field names since GQL doesn't use them like JSON does
0070 LET query$="mutation { productVariantsBulkUpdate(productId: """+gid$+""", "+v$+") { product { id } productVariants { id barcode } userErrors { field message } } }"
0080 LET qry$["query"]=query$; LET post$=DIM(LIST qry${ALL}); DIM qry$
0090 PRINT post$
#8
Programming / Re: REST to GRAPHQL
February 26, 2025, 04:54:55 PM
I always have trouble passing the variables as a separate section, so I just include them in the argument. This also gets rid of the need to define the variables and name the mutation, which lets you get rid of a big chunk of the text in the string.  The text "productVariantsBulkUpdate($variants: [ProductVariantsBulkInput!]!, $productId: "ID!)" really is just naming the function and defining the variables, but that isn't really necessary. 

Here's some code I used to build this mutation.  It builds a JSON string for multiple variants/barcodes to update, then removes the quotes around the field names in the JSON string since GraphQL doesn't use quotes around the field names, and then includes that JSON string as an argument to the function.  The resulting POST$ variable should work for you.

<Posted Code in the next message, since this message is causing it to be formatted incorrectly)
#9
Programming / Re: REST to GRAPHQL
February 21, 2025, 03:08:59 PM
Yes, you always need to escape the quotes if they are internal to the query.  (Your original post on the query has escaped the quotes before and after the GID.)

The PXPlus JSON parser will generally handle the escaping for you, if you just build the query with the quotes in it.  Below is an example of the code I used.  Note that I did NOT escape the quotes, but they are there in the JSON because I created the JSON using the DIM(LIST ) functionality of PXPlus, and it properly escapes any necessary characters.

Also note that, when building the query string, I use two quotes together quite a bit (see line 20).  That just tells PVXPlus to include the quote in the string you are building, rather than treating the quote as the end of the string.  A lot of people don't know that trick, so I'm pointing it out just in case you have trouble deciphering line 20 of the code below.

0010 LET gid$="gid://shopify/InventoryItem/33749329936442"
0020 LET query$="query { inventoryItem (id: """+gid$+""") { id inventoryLevels(first:2) { edges { node { id location { id name } quantities(names: [""available"",""on_hand""]) { name quan
0020:tity } } } } } }"
0030 LET qry$["query"]=query$; LET post$=DIM(LIST qry${ALL})
0040 PRINT post$

-;run

{"query":"query { inventoryItem (id: \"gid://shopify/InventoryItem/33749329936442\") { id inventoryLevels(first:2) { edges { node { id location { id name } quantities(names: [\"available\"
,\"on_hand\"]) { name quantity } } } } } }"}
-;
#10
Programming / Re: REST to GRAPHQL
February 21, 2025, 12:48:48 PM
you need to 'escape' the quotes around "available" by putting a backslash in front of them.  Try the example below.  I added the \ before the quotes, and also added a second inventory quantity so that you can see how the additional quantities can be requested.

{"query":"query { inventoryItem ( id: \"gid://shopify/InventoryItem/41450849599575\") { id inventoryLevels(first:2) { edges { node { id location { id name } quantities(names: [\"available\",\"on_hand\"]) { name quantity } } } } } }" }

#11
Programming / Re: REST to GRAPHQL
February 07, 2025, 02:23:27 AM
I don't use *plus/web/request, since we built SOAP interfaces before PXP came to be, and we've just adapted them to REST.  But, you should be able to use the examples below with the POST data to pass to *plus/web/request.

The endpoint should always be the graphql.json endpoint at https://<STORE_NAME>.myshopify.com/admin/api/<API_VERSION>/graphql.json . The endpoint isn't different for each item you need to retrieve (like customers, or orders, or products) like it is with REST.  You also generally need to refer to every item by its' GID rather than its' ID.

The biggest difference for me is the need to manually build the queries, since I haven't found a simple way to build them, like the DIM() commands can be used to build JSON.  There's a lot of manual "pick and shovel" work (as we old-timers call it!) just typing in the queries and making sure that you don't miss a curly brace somewhere!

Below is an example program of retrieving a customer, using the known GID for the customer.  Note that the admin_api_path$ must contain your URL (ie: https://<STORE_NAME>.myshopify.com/admin/api/<API_VERSION>/).

0010 LET gid$="gid://shopify/Customer/5432101234"
0020 LET query$="query { customer(id: """+gid$+""") { id email numberOfOrders createdAt defaultAddress { id name address1 address2 city provinceCode zip countryCode} }}"
0030 LET qry$["query"]=query$; LET post$=DIM(LIST qry${ALL})
0040 LET type$="POST",url$=admin_api_path$+"/graphql.json"

And here is the full request that is sent to Shopify:
POST https://my_dev_store.myshopify.com/admin/api/2024-07/graphql.json HTTP/1.1
Host: my_dev_store.myshopify.com
Authorization: Basic xMGY4MzYxOThkNzdiZWQyNTljZmI6NGRiZWZkZTUzMzNmOWRmYjgwZjJOWVjMWYxZDAzMDYhYzhkY2NkNWIxZTY=
Connection: Close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: 188

{"query":"query { customer(id: \"gid://shopify/Customer/5432101234\") { id email numberOfOrders createdAt defaultAddress { id name address1 address2 city provinceCode zip countryCode}}}"}

That should return the customer data requested.  The JSON is basically the same as REST, but there is also a "data." structure as the base structure in the JSON.  An example response to the above request is shown below.

{
  "data":{
    "customer":{
      "id":"gid://shopify/Customer/55432101234",
      "email":"test@sample.com",
      "numberOfOrders":"60",
      "createdAt":"2017-04-20T19:10:27Z",
      "defaultAddress":{
        "id":"gid://shopify/MailingAddress/685920161868?model_name=CustomerAddress",
        "name":"Test User",
        "address1":"23456 Oak Hill Drive",
        "address2":"",
        "city":"Laguna Hills",
        "provinceCode":"CA",
        "zip":"92653",
        "countryCode":"US"
      }
    }
  },
  "extensions":{
    "cost":{
      "requestedQueryCost":1,
      "actualQueryCost":1,
      "throttleStatus":{
        "maximumAvailable":2000.0,
        "currentlyAvailable":1999,
        "restoreRate":100.0
      }
    }
  }
}


If you don't know any of your customer GID's, here's a quick query to return the first 10:

0020 LET query$="query { customers(first: 10) {edges {node {id}}}}"
0030 LET qry$["query"]=query$; LET post$=DIM(LIST qry${ALL})
0040 LET type$="POST",url$=admin_api_path$+"/graphql.json"

The full post data is shown below:
{"query":"query { customers(first: 10) {edges {node {id}}}}"}

Hope that's enough info to get you started! 


#12
Programming / Re: REST to GRAPHQL
January 14, 2025, 03:43:57 PM
I'm doing the same thing, with PXPlus and Shopify.  I've been using some of the GraphQL functionality for a couple of years since some of the functionality I needed was only available via GraphQL.  Now that Shopify is moving everything to GraphQL, I'm gradually converting the calls, so I'm fairly familiar with it.  REST is certainly easier to program with PXPlus using the JSON functionality in the DIM () function.  I haven't figured out a simple way to do the GraphQL calls other than just hard-coding all of the field names and such that you need in the response.  Once you get the data back, the structure is essentially the same as the REST data, except it only contains the fields that you requested rather than the entire record.  So, I'm finding myself digging through all of my code trying to find every field name that I always relied on to be there automatically, since now I need to specifically request all of those field names (after confirming that the field name isn't changed in GraphQL, which happens far too often!)  Feel free to ask any questions you might have, and I'll see if I can answer them.  Could be good info for a lot of people that might need to work with GraphQL in the future, since that seems to be becoming more prominent in various web interfaces.
#13
Nomads / Re: Left-Justify BUTTON Text
November 06, 2024, 10:08:30 PM
Check the manual page for the OPT= options for a BUTTON at:

https://manual.pvxplus.com/PXPLUS/directives/button.htm#Mark4

There are some caveats discussed there, such as the need for 3D or 4D if using button images.  In Nomads, there is a radio button on the Font screen, but that might not work if you don't 3D enabled.
#14
Language / Re: Json Parsing
November 01, 2024, 04:33:02 PM
The "#6" is likely due to the fact that the format ".6" in PXP would indicate the 6th item in an array.  Eg:

Line.1.amount
Line.2.amount
...
Line.6.amount

Since you have a new element named "6", the JSON parser in PXP would normally create that as "Extra Data.Number of Bulbs.6.desc", but that would be confusing since it addressing it as such would indicate an array. 

My suggestion would be that you prefix or suffix something with the 6 so that it isn't just a raw number.
#15
Language / Re: Json Parsing
November 01, 2024, 04:24:47 PM
Use DIM(KEY ...) to get the names of the JSON fields.  Below is an example using your data.  The odd thing is that the one level is "#6" instead of just "6" - I've never seen that before!

0010 BEGIN
0020 LET json$=$7B22564974656D4944223A2236313337222C2245787472612044617461223A7B224E756D626572204F662042756C6273223A7B2236223A7B2264657363223A2236222C2269636F6E62617365223A6E756C6C2C2269636F6E223A6E756C6C2C22756E697473223A2242756C6273227D7D7D7D$
0030 DIM LOAD arr${ALL}=json$
0040 LET c=DIM(READ NUM(arr$))
0050 FOR i=1 TO c
0060 PRINT "arr$["+QUO+DIM(KEY arr$)+QUO+"] = "+arr$
0070 NEXT

RUN
arr$["VItemID"] = 6137
arr$["Extra Data.Number Of Bulbs.#6.desc"] = 6
arr$["Extra Data.Number Of Bulbs.#6.iconbase"] = null
arr$["Extra Data.Number Of Bulbs.#6.icon"] = null
arr$["Extra Data.Number Of Bulbs.#6.units"] = Bulbs