hasura / kriti-lang

A minimal JSON templating language
Apache License 2.0
56 stars 9 forks source link

Support if statements inside of a hash #61

Open LucasBrandt opened 2 years ago

LucasBrandt commented 2 years ago

I am trying to use request body transformation in a Hasura custom action to have the action accept multiple optional parameters, and then deliver a transformed request built from those parameters to an existing API.

I'd like for consumers of this custom action to have the option of passing some or all of the parameters. I'm having trouble supporting this in a sustainable way, because I can't use if/else statements inside of the hash I'm building.

Here's an example of what I'd like to be able to do in the request body transformation:

{
  "id": {{$body.input.id}},
  {{ if $body.input.article.name }}
    "name": {{$body.input.article.name}},
  {{ end }}
  {{ if $body.input.article.description }}
    "description": {{$body.input.article.description}},
  {{ end }}
  "foo": "bar"
}

That currently results in an error: Parse Error: Unexpected token '{{' at the "foo": "bar" line.

Kriti provides some workarounds that I've considered, but they have drawbacks:

Thanks!

sordina commented 2 years ago

Hi @LucasBrandt

Sorry for the delayed reply.

The issue here is that the template isn't text, but instead structural. We're planning to add a "spread" style operator to make this more in line with what you would do in JS, but for now there are some functions available that should allow you to construct the object you are looking for like so:

fromPairs(concat([
  [["id", "foo"]],
  {{ if $body.input.article.name }}
    [["name", $body.input.article.name]]
  {{ else }}
    []
  {{ end }},
  {{ if $body.input.article.description }}
    [["description", $body.input.article.description]]
  {{ else }}
    []
  {{ end }},
  [["foo", "bar"]]
]))

Obviously this isn't ideal, but should be able to be used as a workaround in this situation until more purpose fit functionality is available. We may also extend the removeNulls function to work on objects in addition to arrays so it could be used to remove fields with null values.

LucasBrandt commented 2 years ago

@sordina Thanks for pointing me in the right direction! I'll give the fromPairs approach a try for the next custom action I create which would ideally support multiple optional parameters, but this does look like it will work well for my needs.

LucasBrandt commented 2 years ago

Thanks again @sordina!

The solution you suggested was an improvement, but not enough to fully support forming an API request with optional parameters using request body transformation. I was not able to use {{ if $body.input.article.name }} because that isn't a boolean value. I tried using {{ if empty($body.input.article.name) }} but that lead me to the broader realization I should have had from the start: I really would need a function more like "is this defined" than "is this truthy" to really support using request body transformation to transform requests and send them to an existing API. That's because I'd need users of this custom action to be able to pass null or false or "" as values for attributes - for example, if a user wants to unset an article's name entirely or set it to an empty string.

With my now-improved understanding of the problems I'm facing, I think they're outside the scope of this feature request, so feel free to close this one if you'd like. I'll probably open an issue in graphql-engine instead. There are some similar issues (https://github.com/hasura/graphql-engine/issues/7909 and https://github.com/hasura/graphql-engine/issues/8574) but they don't address quite the same needs.

Thanks for the help!