blackstork-io / fabric

An open-source command-line tool for reporting workflow automation and a configuration language for reusable templates. Reporting-as-Code
https://blackstork.io/fabric/
Apache License 2.0
10 stars 0 forks source link

Make row query explicit in `content.table` #169

Open traut opened 2 weeks ago

traut commented 2 weeks ago

Description

Currently, content.table relies on query to return an iterable collection for the table rows. This is implicit logic that is not clear to the users.

Design

Introduce row_query attribute for content.table content provider -- an explicit query that must return an iterable collection.

Example:


content table "my_table" {
  query = ".data.inline.findings[0].name"

  row_query = <<-EOT
     ["aaa", "bbb", "ccc", .query_result]
  EOT

  columns = [
    { header = "Header 1", value = "."},
    { header = "Header 2", value = "static-value"},
  ]
}

[!WARNING] row_query, being plugin-specific attribute,

  • should be executed in inside the plugin
  • must be executed after query query is evaluated (if it is provided) and .query_result is available in the context.

In addition, introduce row_index variable, available in the value string templates, that represents the current index of the row.

Andrew-Morozko commented 1 week ago

introduce row_index variable

@traut Are we ignoring the possibility of row_index being already present in the user's row object? Perhaps we should prefix our variables (fabric_row_index) to try to avoid collisions, or we may go even further by creating a row_index function that has no chance of shadowing user's data

More crucially: what is the shape of the template data? If we're executing template as-is, on each row returned from jq query, then:

content table "my_table" {
  row_query = <<-EOT
     ["aaa", "bbb", "ccc"]
  EOT

  columns = [
    { header = "Header 1", value = "."},
  ]
}

this renders a 1-column table with cells "aaa", "bbb", "ccc", but there's no way to add a .row_index, . is of type string. We can put the items of the row_query as .row_data which would change for every row (and now there's no possibility of name collisions):

content table "my_table" {
  row_query = <<-EOT
     ["aaa", "bbb", "ccc"]
  EOT

  columns = [
    { header = "Row #", value = ".row_index"},
    { header = "Header 1", value = ".row_data"},
  ]
}

One more question: should the .query_result and the rest of the data context be still accesible for row templates? It definately should for header templates, otherwise header templates have no data at all.

So I guess the template execution context looks something like this?

{
    "row_index": "number for row templates, missing for header templates",
    "row_data": "(whatever row_query returned)[row_index] for row templates, missing for header templates",
    "query_result": "original query result passed through",
    "the rest of the": "data context"
}

must return an iterable collection.

Can we clarify something here? ["aaa", "bbb", "ccc"] returns one result, it being the list ["aaa", "bbb", "ccc"]. But jq supports returning multiple results, for example ["aaa", "bbb", "ccc"][] would return the 3 strings as 3 separate results. You have better knowlege of how jq feels, which is the better user experience?