A style guide and linter for Looker's LookML data modeling language
View the Project on GitHub looker-open-source/look-at-me-sideways
LAMS comes pre-bundled with a number of rules that reflect our opinionated best practices. However, sometimes you may want to implement your own rules that may be highly specific to your project/model.
To handle this, LAMS offers the ability to extend it with custom rules written in a LISP-based expression, specified in a manifest.
Expression-based rules can be declared within your manifest’s LookML. By way of example:
# in manifest.lkml
# LAMS
# rule: prod_connection {
# description: "Models must use prod connection"
# match: "$.model.*"
# expr_rule: ( === ::match:connection "prod" ) ;;
# }
Alternately, you can maintain them in YAML for nicer syntax:
# in a YAML file passed to the `manifest` argument
# (requires installing js-yaml)
rule:
prod_connection:
description: Models must use prod connection
match: "$.model.*"
expr_rule: |
( === ::match:connection "prod" )
You can quickly experiment with custom rule definitions in the Rule Sandbox
Here is what each part of the rule definition means:
match
: The value matched by the match expression. (The expression is invoked once for each time the pattern is matched in your project)path
: An array containing the path to the matched LookML. For example, ['$','model','my_model']
project
: The entire LookML project, in case you need to further reference any data from it within each matchSecurity Disclaimer: Expression evaluation is powered by the Liyad library. It both intends to prevent escalation of privleges, and, unlike Javascript evaluation, theoretically can do so. (See my writeup on this). However, it is still a very new project, so evaluate it accordingly.
The match
property accepts a JSON Path expression. Further documentation about the JSON Path syntax can be found in the docs of the underlying library jsonpath-plus.
Additionally, to avoid potential code-execution issues, LAMS limits filtering expressions to a property existence check, or a single equality or inequality operation between a property and a string, boolean, or undefined literal.
Note that while you can use unrooted paths to match things a bit more concisely (i.e. without the leading $.), using a rooted path means less ambiguity in case a LookML parameter is also valid in another context.
JSONpath | Description |
---|---|
$.model.* |
All models |
$.file.*.view.* |
All views across all files (once per declaration) |
$.model.*.view.* |
All views included in models (once per inclusion) |
$.model.*.explore.* |
All explores across all models |
$.model.foo.explore.* |
All explores in the foo model |
$.model.*.view.*[dimension,measure].* |
All dimensions and all measures across all models |
$.model[?(@.persist_for)] |
All models that declare persist_for |
(=== ::match:value_format_name "usd")
(!== ::match:type undefined)
E.g., does the label start with “Is” or “Has”? (Note that $match
is a function, and match
is the matched value)
($boolean ($match
"^Is |^Has "
::match:label
))
($all
(=== ::match:hidden true)
(=== ::match:extension_required true)
)
($any
(=== ::match:hidden true)
(=== ::match:extension_required true)
...
)
($let myvar (+ ::match:foo "_bar") )
(=== myvar "foo_bar")
($let urls ($map links (-> (link) ::link:url)))
($let badUrls ($filter urls (-> (url) ($match "^http://" url))))
($if ::badUrls:length
($concat "Links must not use insecure HTTP: " ($join ::badUrls ", "))
true
)
The below example calls JavaScript’s native String.prototype.localeCompare method for sorting:
($let dim-names ($object-keys ($any ::match:dimension (#))))
($let alphabetically (-> (a b) ($__call a localeCompare b)))
($let sorted-names ($sort dim-names alphabetically))
(== ($join dim-names ",") ($join sorted-names ","))
Disclaimer: The use of javascript rules are a security tradeoff. Fundamentally, evaluating Javascript code on your local machine and/or CI server gives that code a lot of access. And, this code can be altered by anyone with access to the server on which it is hosted, as well as any LookML developer with access to develop in the LookML project (even without deploy permissions). For this reason, expression based rules are preferred, and javascript-based rules may be deprecated in the future.
If you nevertheless want LAMS to execute Javascript-based custom rules:
custom_rules: ["<url>"]
(inside a #LAMS
comment block)--allow-custom-rules
startup flag