Yeah works with postman and curl and any other graphql client.
Question in regards to running a docker container? Can I use specific events to trigger the docker container to run the script. For example when a lease is bound to a MAC?
Yeah works with postman and curl and any other graphql client.
Question in regards to running a docker container? Can I use specific events to trigger the docker container to run the script. For example when a lease is bound to a MAC?
It not so easy with container if it’s an event like dhcp-client where this script lives. I doubt there is a bug in /tool/fetch here… but one wrong escape char in query, it ain’t going to work.
If it works in Postman, with the leading 0, can you cut-and-paste Postman’s HTTP and cURL “Code snippet” for same GraphQL call?
To do this in Postman, you hit the “</>” icon on right-side toolbar, then should be a drop down for the language to use. Pick “HTTP”, and save that, then pick “cURL” and save that.
If you post the code snippet (excluding auth stuff and replace DNS names with example.com) here, I’m sure we can figure this out using /tool/fetch. That is your best option if need is in dhcp-client script.
Sure. Here is in HTTP. I removed the domain and Bearer token.
POST /api/graphql HTTP/1.1
Host: example.com
{"query":"query search {inventory_model_field_data(general_search:\"000000000000\"){entities{inventory_item_id}}}","variables":{}}
And In CURL
curl -L -X POST 'example.com' -H 'Authorization: Bearer -H 'Content-Type: application/json' --data-raw '{"query":"query search {inventory_model_field_data(general_search:\"000000000000\"){entities{inventory_item_id}}}","variables":{}}'
Now If I try to run the same in Mikrotik like this,
:global datafile1 "{\"query\":\"query inventory{inventory_model_field_data(general_search:\"000000000000\"){entities{id}}}\"}"
:put ([/tool fetch url=$url http-method=post http-header-field="Content-Type:application/json, Authorization: $authorization" http-data=$datafile1 mode=https output=user as-value]->"data")
I just get an empty array response
[]
If I code in the “000000000000” in a variable It seems like it has an issue with the leading 0.
:global testname "000000000000"
:global datafile1 "{\"query\":\"query inventory{inventory_model_field_data(general_search:$testname){entities{id}}}\"}"
:put ([/tool fetch url=$url http-method=post http-header-field="Content-Type:application/json, Authorization: $authorization" http-data=$datafile1 mode=https output=user as-value]->"data")
I get the following response:
{"errors":[{"message":"Syntax Error: Invalid number, unexpected digit after 0: \"0\""}]}
If the value of $testname starts with 1 tho, it seems like it has no problem at all querying this info. I did multiple rounds of test to see if it was a length of the 0s or any other thing. The only answer I got is that it has a problem with the leading 0.
:global testname "100000000000"
:global datafile1 "{\"query\":\"query inventory{inventory_model_field_data(general_search:$testname){entities{id}}}\"}"
:put ([/tool fetch url=$url http-method=post http-header-field="Content-Type:application/json, Authorization: $authorization" http-data=$datafile2 mode=https output=user as-value]->"data")
and the response :
{"data":{"inventory_model_field_data":{"entities":[{"id":"832"}]}}
Your HTTP example is useful. HTTP bodies do NOT need any escaping, but GraphQL must want the " with your leading 0 case. And you can see that cURL use the single quotes, so quotes shouldn’t need escaping there either.
So I think the issue is in RouterOS you need a “triple backsplash”… i.e. \ to include the backslash in data and 3rd backslash for the double quote ".
e.g. This Postman HTTP version
{"query":"query search {inventory_model_field_data(general_search:\"000000000000\"){entities{inventory_item_id}}}","variables":{}}
becomes the following for RouterOS, using same example from Postman test.
:global httpdata "{\"query\":\"query search {inventory_model_field_data(general_search:\\\"000000000000\\\"){entities{inventory_item_id}}}\",\"variables\":{}}"
Basically adding outer double-quote " …HTTP GQL data… " to the HTTP version from Postman, and then for any double quotes inside need a \ in front, as would any backslash \ need to be escape for RouterOS as \. Also if the original query used a $ (that was NOT a routeros variable), that too need escaping from Postman’s version, e.g. $ as the prevent RouterOS from interpolating a local/global variable.
You’ll note that in the cURL and HTTP ones, the data provided to GraphQL backend using search not inventory as the operand & Postman includes a empty variable list. Since your RouterOS work, likely these aren’t at issues.
So if we take your existing query that works, other than the leading 0 problems, add the “triple backslash”, it becomes (with a couple debug :put lines):
:global testname “000000000000”
:put “$testname $[:typeof $testname]”
:global datafile1 “{"query":"query inventory{inventory_model_field_data(general_search:\"$testname\"){entities{id}}}"}”
:put $datafile1
#check to make sure valid JSON…
:put [:serialize to=json [:deserialize from=json $datafile1]]
:put ([/tool fetch url=$url http-method=post http-header-field=“Content-Type:application/json, Authorization: $authorization” http-data=$datafile1 mode=https output=user as-value]->“data”)
FWIW, another way of building the GQL string is using RouterOS array to store it, and the use [:serialize] to convert RouterOS array to JSON, this avoid some of the more complex escaping (and uses a { } block so :local variables can be used at the CLI, since you’d want to use :local variables a final dhcp-client script:
{
:local searchtext "000000000"
:local gurl "...."
:local auth "...."
:local header "Content-Type:application/json, Authorization: $auth"
:local gql [:toarray ""]
:set ($gql->"query") "query inventory{inventory_model_field_data(general_search:\"$searchtext\"{entities{id}}}"
# or using variables, to do variables on the GQL backend....
#:set ($gql->"variables") [:toarray ""]
#:set ($gql->"variables"->"searchtext") $searchtext
#:set ($gql->"query") "query inventory{inventory_model_field_data(general_search: \$searchtext {entities{id}}}"
:local httpdata [:serialize to=json $gql]
:put $httpdata
:local fetched [/tool fetch url=$gurl http-method=post http-header-field=$header http-data=$httpdata mode=https output=user as-value]
:put $fetched
:local results [:deserialize from=json ($fetched->"data")]
:put $results
:put ($results->"data"->"inventory_model_field_data"->"entities"->0->"id")
}
Sorry for the late reply. Just wanted to tell you how much I appreciate you helping me out with all the issues I had. I am all good now. Thanks
I suspect the \" fixed it in the string that’s being sent. Since my 2nd example above using [:serialize to=json]* would NOT actually work for your 000000 case…
i.e. I learned something new here actually. Apparently [:serialize to=json] will automatically convert strings into numbers - even if the RouterOS type is a string and WITHOUT an explicit [:tonum] convertion. Specifically,
:put [:serialize to=json "0000000000"]
0.000000
Note the string “0000000000” is parsed to a JSON floating point number, which break the example GraphQL since it for sure isn’t expecting a floating point type.
Basically [:serialize] will parse a string type to see if it some kinda of number, and if it’s a number it will make it a floating point type in the JSON. I didn’t actually know this, but it does meaning floating point numbers can be expressed in JSON in a call to /tool/fetch. But does mean you CANNOT provide a string “00000000” to :serialize to get JSON from RouterOS variables/arrays - you building the GraphQL JSON in a string, with the needed escapes, is the right approach with “0000000” since the other end does want a string, not a number.
Just to document what happens using the [:serialize] approach – which work in 99.9% of normal use cases, just NOT if you needed “00000000” string in JSON ![]()
# string that contain only ints or ".", become a floating point number type in JSON
:put [:serialize to=json "123"]
# 123.000000
:put [:serialize to=json "123.123"]
# 123.123000
# and specifically to the OP's case...
:put [:serialize to=json "0000000000"]
0.000000
# "num" RouterOS variable types, so remain ints
:put [:serialize to=json 123]
# 123
:put [:serialize to=json {a=123}]
# {"a":123}
# weird case, since 123.123 is an "ip" variable type in RouterOS...
# an IP address "pre-CIDR" could skip .0 in middle like :: in IPv6)....
# so this is correct since the JSON does not have an "ip" type, thus string
# and 123.123 is same IP address as more common 123.0.0.123 form, which likely be expected in most systems.
:put [:serialize to=json 123.123]
# "123.0.0.123"