Skip to content

Commit fe28674

Browse files
committed
feat(jinja): add b64decode/b64encode builtin filters
Core SQLMesh shipped no builtin Jinja filters, so base64-encoded secrets (e.g. a BigQuery service-account key stored in an env var) could not be decoded directly in config YAML. Add b64decode/b64encode and register them on the shared environment factory so they are available both in config YAML and in models, mirroring ansible's filters. Closes #5754 Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
1 parent 991a327 commit fe28674

2 files changed

Lines changed: 37 additions & 2 deletions

File tree

sqlmesh/utils/jinja.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import base64
34
import importlib
45
import json
56
import re
@@ -28,11 +29,30 @@
2829
SQLMESH_JINJA_PACKAGE = "sqlmesh.utils.jinja"
2930

3031

32+
def b64decode(value: t.Union[str, bytes]) -> str:
33+
decoded = value.encode("utf-8") if isinstance(value, str) else value
34+
return base64.b64decode(decoded).decode("utf-8")
35+
36+
37+
def b64encode(value: t.Union[str, bytes]) -> str:
38+
encoded = value.encode("utf-8") if isinstance(value, str) else value
39+
return base64.b64encode(encoded).decode("utf-8")
40+
41+
42+
def create_builtin_filters() -> t.Dict[str, t.Callable]:
43+
return {
44+
"b64decode": b64decode,
45+
"b64encode": b64encode,
46+
}
47+
48+
3149
def environment(**kwargs: t.Any) -> Environment:
3250
extensions = kwargs.pop("extensions", [])
3351
extensions.append("jinja2.ext.do")
3452
extensions.append("jinja2.ext.loopcontrols")
35-
return Environment(extensions=extensions, **kwargs)
53+
env = Environment(extensions=extensions, **kwargs)
54+
env.filters.update(create_builtin_filters())
55+
return env
3656

3757

3858
ENVIRONMENT = environment()

tests/utils/test_jinja.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

3-
from sqlmesh.utils import AttributeDict
3+
from base64 import b64encode
4+
5+
from sqlmesh.utils import AttributeDict, yaml
46
from sqlmesh.utils.jinja import (
57
ENVIRONMENT,
68
JinjaMacroRegistry,
@@ -329,3 +331,16 @@ def test_macro_registry_to_expressions_sorted():
329331
== "refs = {'orders': {'database': 'jaffle_shop', 'nested_list': ['a', 'b', 'c'], 'schema': 'main'}, 'payments': {'database': 'jaffle_shop', 'nested': {'baz': 'bing', 'foo': 'bar'}, 'schema': 'main'}}\n"
330332
"sources = {}"
331333
)
334+
335+
336+
def test_builtin_base64_filters():
337+
encoded = b64encode(b"secret").decode("utf-8")
338+
339+
env = JinjaMacroRegistry().build_environment()
340+
assert env.from_string("{{ value | b64decode }}").render(value=encoded) == "secret"
341+
assert env.from_string("{{ 'secret' | b64encode }}").render() == encoded
342+
assert env.from_string("{{ 'secret' | b64encode | b64decode }}").render() == "secret"
343+
344+
# The same filters are available when rendering Jinja in config YAML files.
345+
config = yaml.load(f'env_vars:\n TOKEN: "{{{{ "{encoded}" | b64decode }}}}"')
346+
assert config == {"env_vars": {"TOKEN": "secret"}}

0 commit comments

Comments
 (0)