Reports

The lineage report family accepts selector and optional direction and level variables and is available as HTML, CSV, Markdown, text, XLSX and PDF.

bluep report renders text from the model through a small template language. Seven templates are built in (CSV, Markdown and HTML); custom templates are plain files. Output is deterministic: no timestamps, no absolute paths, no environment values.

Command line

bluep report <dir> --template <name> [--format html|csv|md|txt] [--out <file>] [--var k=v]...
bluep report <dir> --template-file <path> [--format html|csv|md|txt] [--out <file>] [--var k=v]...
bluep report --list
  • --template names a built-in (see --list); --template-file reads a custom template.
  • The output format decides the automatic escaping (below). It defaults from the extension of the template name (.html/.htm, .csv, .md/.markdown, .txt), otherwise txt.
  • --out writes the result to a file instead of standard output.
  • --var k=v defines a string variable available as vars.k (repeatable).
  • Exit codes: 0 success, 2 usage error, 3 template error or model error. Template errors are reported as <template>:<line>:<column>: <message>.

Built-in templates

name format content
classification.html html matrix of classification levels with the entities and attributes at each level
columns.csv csv one row per physical column: target, schema, table, type, nullability, default, mapping, classifications
dictionary.md md data dictionary with one section per entity: attributes, primary and unique keys
entities.csv csv one row per entity: name, description, attribute count, stereotype, subject areas
glossary.md md glossary terms with definitions, aliases and the entities and attributes using them
lint.md md lint findings grouped by rule
relationships.md md relationships with columns, cardinalities and referential actions

Their sources live in crates/bluep-core/templates/ and are good starting points for custom templates.

Template language

A template is text with two kinds of markers:

  • {{ expression }} prints a value;
  • {% statement %} controls the flow: for, endfor, if, elif, else, endif, set.

A - directly inside a marker trims whitespace next to it: {{- and {%- remove spaces and tabs and at most one newline before the marker; -}} and -%} remove spaces and tabs and at most one newline after it. Putting a statement on its own line and ending it with -%} makes the whole line disappear from the output.

There is no comment syntax. To print a literal {{, write {{ "{{" }}.

Statements

{% for item in model.relationships %}...{% endfor %}        iterate the values of an object (sorted keys) or an array
{% for name, entity in model.entities %}...{% endfor %}     also bind the key (object) or the 0-based index (array)
{% if expr %}...{% elif expr %}...{% else %}...{% endif %}
{% set total = total + 1 %}                                 assign a variable; persists after loops end

Inside a loop, loop.index (1-based), loop.first, loop.last and loop.length are available. Looping over null (for example a missing optional key) produces no iterations. Loop variables shadow outer variables and are restored when the loop ends.

Expressions

  • Literals: 'text' or "text" (escapes \\, \', \", \n), integers, true, false, null.
  • Paths: model.entities.customer.attributes[0].name, vars.owner, items.length. A missing nested key yields null; a missing top-level variable is an error.
  • Operators, from loosest to tightest: or, and, not, comparisons (== != < <= > >=), + and -. + concatenates two strings or adds two integers. < and friends compare two integers numerically or two strings byte-wise; anything else is an error. == compares structurally.
  • Truthiness: false, null, 0, "", empty arrays and empty objects are false.
  • Function calls: len(x), join(list, ", "), ...
  • Filters: {{ value | upper | truncate(20) }} — x | f(a, b) is f(x, a, b).

Functions

function result
len(x) number of elements, keys or characters; 0 for null
keys(obj) / values(obj) the keys / values of an object as arrays
sorted(arr) strings sorted byte-wise, or objects sorted by their name
sort_by(arr, key) stable sort of objects by a key
join(arr, sep) elements joined by sep
default(x, d) d when x is null, else x
contains(c, x) element of an array, substring of a string, or key of an object
has(obj, key) object has the key
lower(s), upper(s), trim(s) string operations
replace(s, a, b), split(s, sep) string operations
snake(s), pascal(s), camel(s) case conversion
pad(s, n), truncate(s, n) right-pad with spaces / keep the first n characters
str(x) string form; compact JSON for non-strings
type_text(x) the model's text form of a type (decimal(12, 2), domain email, varchar(320)); x may be an attribute or column object or its ty value
default_text(x) the model's text form of a default (0, "pending", expr "now()"); x may be an attribute or column object or its default value

All functions are also filters. Two filters have no function form: raw disables the automatic escaping of the value, escape applies it explicitly.

Values printed

Strings print as they are, integers in decimal, booleans as true/false, null as nothing, arrays and objects as compact JSON.

Automatic escaping

Every {{ }} output is escaped for the output format unless its filter chain contains raw:

  • html: & < > " ' become &amp; &lt; &gt; &quot; &#39;;
  • csv: a value containing a comma, a double quote, a newline or a leading or trailing space is wrapped in double quotes, with inner quotes doubled;
  • md: | becomes \| and newlines become spaces;
  • txt: nothing.

The context

variable content
model the model as JSON, exactly the shape of bluep export json (see docs/json-format.md): model.entities, model.relationships, model.subject_areas, model.domains, model.enums, model.glossary, model.targets, model.classifications, ...
meta {"name", "version", "description"} of the model
lint array of lint findings {"rule", "severity", "kind", "object", "message", "file", "line"} in report order
vars the --var definitions as strings

Example

owners.md:

# Entities owned by {{ vars.owner }}
{% for name, e in model.entities -%}
{% if default(e.properties.owner, "") == vars.owner %}
- `{{ name }}`: {{ default(e.description, "") }} ({{ len(e.attributes) }} attributes)
{%- endif -%}
{% endfor %}
bluep report devdata/models/saas --template-file owners.md --var owner=billing

Errors

Parse errors: unterminated '{{', unterminated '{%', 'endfor' without 'for', 'endif' without 'if', 'for' without 'endfor', expected identifier after 'for', unknown statement 'foo', unexpected token ')', unterminated string literal. Evaluation errors: unknown variable 'x', unknown function 'f', unknown filter 'f', cannot iterate over string, cannot add string and integer, cannot compare array with integer, len() expects 1 argument(s), found 2. All are reported with the template name, line and column of the offending marker or token.

Limitations

No macros, includes, inheritance, floating-point arithmetic, regular expressions or user-defined functions. Templates read the model; they cannot change it.