From 926baa06be3653e682f4a578b9f7e959e04dd417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Egelund-M=C3=BCller?= Date: Thu, 19 Dec 2024 14:05:30 +0100 Subject: [PATCH] Add `namespace:` for resources (#6306) --- runtime/compilers/rillv1/parse_node.go | 8 +++++ runtime/compilers/rillv1/parser_test.go | 44 +++++++++++++++++++++++++ runtime/compilers/rillv1/util.go | 3 ++ 3 files changed, 55 insertions(+) diff --git a/runtime/compilers/rillv1/parse_node.go b/runtime/compilers/rillv1/parse_node.go index 8beea576f6d..0b6d0d973da 100644 --- a/runtime/compilers/rillv1/parse_node.go +++ b/runtime/compilers/rillv1/parse_node.go @@ -73,6 +73,9 @@ type commonYAML struct { Kind *string `yaml:"kind"` // Name is usually inferred from the filename, but can be specified manually. Name string `yaml:"name"` + // Namespace is an optional value to group resources by. + // It currently just gets pre-pended to the resource name in the format `/`. + Namespace string `yaml:"namespace"` // Refs are a list of other resources that this resource depends on. They are usually inferred from other fields, but can also be specified manually. Refs []yaml.Node `yaml:"refs"` // ParserConfig enables setting file-level parser config. @@ -283,6 +286,11 @@ func (p *Parser) parseStem(paths []string, ymlPath, yml, sqlPath, sql string) (* } } + // If a namespace was provided in YAML, prepend it to the name. + if cfg != nil && cfg.Namespace != "" { + res.Name = cfg.Namespace + ":" + res.Name + } + // If resource kind is not set in YAML or SQL, try to infer it from the context if res.Kind == ResourceKindUnspecified { if strings.HasPrefix(paths[0], "/sources") { diff --git a/runtime/compilers/rillv1/parser_test.go b/runtime/compilers/rillv1/parser_test.go index 4feaaeb743b..200af087d30 100644 --- a/runtime/compilers/rillv1/parser_test.go +++ b/runtime/compilers/rillv1/parser_test.go @@ -2022,6 +2022,50 @@ refresh: requireResourcesAndErrors(t, p, []*Resource{m1, m2}, nil) } +func TestNamespace(t *testing.T) { + ctx := context.Background() + repo := makeRepo(t, map[string]string{ + `rill.yaml`: ``, + `models/m1.yaml`: ` +type: model +sql: SELECT 1 +`, + `explores/e1.yaml`: ` +type: explore +namespace: foo +metrics_view: missing +`, + }) + + resources := []*Resource{ + { + Name: ResourceName{Kind: ResourceKindModel, Name: "m1"}, + Paths: []string{"/models/m1.yaml"}, + ModelSpec: &runtimev1.ModelSpec{ + RefreshSchedule: &runtimev1.Schedule{RefUpdate: true}, + InputConnector: "duckdb", + InputProperties: must(structpb.NewStruct(map[string]any{"sql": `SELECT 1`})), + OutputConnector: "duckdb", + }, + }, + { + Name: ResourceName{Kind: ResourceKindExplore, Name: "foo:e1"}, + Paths: []string{"/explores/e1.yaml"}, + Refs: []ResourceName{{Kind: ResourceKindMetricsView, Name: "missing"}}, + ExploreSpec: &runtimev1.ExploreSpec{ + DisplayName: "Foo: E1", + MetricsView: "missing", + DimensionsSelector: &runtimev1.FieldSelector{Selector: &runtimev1.FieldSelector_All{All: true}}, + MeasuresSelector: &runtimev1.FieldSelector{Selector: &runtimev1.FieldSelector_All{All: true}}, + }, + }, + } + + p, err := Parse(ctx, repo, "", "", "duckdb") + require.NoError(t, err) + requireResourcesAndErrors(t, p, resources, nil) +} + func requireResourcesAndErrors(t testing.TB, p *Parser, wantResources []*Resource, wantErrors []*runtimev1.ParseError) { // Check errors // NOTE: Assumes there's at most one parse error per file path diff --git a/runtime/compilers/rillv1/util.go b/runtime/compilers/rillv1/util.go index 150c56056cc..4d98bdafe6e 100644 --- a/runtime/compilers/rillv1/util.go +++ b/runtime/compilers/rillv1/util.go @@ -18,6 +18,9 @@ func ToDisplayName(name string) string { name = strings.ReplaceAll(name, "_", " ") name = strings.ReplaceAll(name, "-", " ") + // Replace colons with colon-space. + name = strings.ReplaceAll(name, ":", ": ") + // Capitalize the first letter. name = cases.Title(language.English).String(name)