diff --git a/cmd/hclexp/web.go b/cmd/hclexp/web.go index 9f6cc5a..3435525 100644 --- a/cmd/hclexp/web.go +++ b/cmd/hclexp/web.go @@ -409,26 +409,27 @@ type indexData struct { } type objectData struct { - Title string - Base string // URL prefix for links (manifest mode); "" in single mode - Label string // schema label shown in the nav (manifest mode); "" otherwise - Database string - Kind string - KindLabel string - Name string - View string // "html" | "ddl" | "hcl" - HTMLHref string - DDLHref string - HCLHref string - Props []kv - Tables []tableSection - Query string - RawSQL string - Code string // rendered DDL or HCL for those views - FlowAnchor string // anchor on /flows when this object participates in a flow - Problems []problemView - DependsOn []objLink - DependedBy []objLink + Title string + Base string // URL prefix for links (manifest mode); "" in single mode + Label string // schema label shown in the nav (manifest mode); "" otherwise + Database string + Kind string + KindLabel string + Name string + View string // "html" | "ddl" | "hcl" + HTMLHref string + DDLHref string + HCLHref string + Props []kv + Tables []tableSection + Projections []projectionView + Query string + RawSQL string + Code string // rendered DDL or HCL for those views + FlowAnchor string // anchor on /flows when this object participates in a flow + Problems []problemView + DependsOn []objLink + DependedBy []objLink } // columnCollapseLimit is the row count above which a collapsible column list is @@ -563,6 +564,7 @@ func (s *webServer) buildHTMLView(data *objectData, db *hclload.DatabaseSpec, ki if sec, ok := constraintsSection(t.Constraints); ok { data.Tables = append(data.Tables, sec) } + data.Projections = projectionViews(t.Projections) case hclload.KindMaterializedView: mv := findMaterializedView(db, name) if mv == nil { @@ -770,6 +772,26 @@ func constraintsSection(cs []hclload.ConstraintSpec) (tableSection, bool) { return sec, true } +// projectionView is a table projection rendered on the object page: its name and +// canonical SELECT, shown as a query block like a materialized view's query. +type projectionView struct { + Name string + Query string +} + +// projectionViews maps a table's projections to their rendered views. Returns +// nil when the table has none (the template omits the section). +func projectionViews(ps []hclload.ProjectionSpec) []projectionView { + if len(ps) == 0 { + return nil + } + views := make([]projectionView, 0, len(ps)) + for _, p := range ps { + views = append(views, projectionView{Name: p.Name, Query: p.Query}) + } + return views +} + func dictAttributesSection(attrs []hclload.DictionaryAttribute) tableSection { sec := tableSection{Title: "Attributes", Headers: []string{"name", "type", "default", "expression"}} for _, a := range attrs { diff --git a/cmd/hclexp/web/object.html b/cmd/hclexp/web/object.html index f5b444a..52b6f5b 100644 --- a/cmd/hclexp/web/object.html +++ b/cmd/hclexp/web/object.html @@ -41,6 +41,10 @@

{{.Title}}{{if .Collapsible}} {{len .Rows}}{{end} {{end}} + {{if .Projections}}

Projections

+ {{range .Projections}}

{{.Name}}

{{.Query}}
{{end}} + {{end}} + {{if .Query}}

Query

{{.Query}}
{{end}} {{if .RawSQL}}

SQL

{{.RawSQL}}
{{end}} {{else}} diff --git a/cmd/hclexp/web_sections_test.go b/cmd/hclexp/web_sections_test.go index c01f484..456c5e8 100644 --- a/cmd/hclexp/web_sections_test.go +++ b/cmd/hclexp/web_sections_test.go @@ -202,6 +202,15 @@ func TestWebSections_ConstraintsSection(t *testing.T) { }, sec) } +func TestWebSections_ProjectionViews(t *testing.T) { + assert.Nil(t, projectionViews(nil)) + + views := projectionViews(sectionsSchema().Databases[0].Tables[0].Projections) + assert.Equal(t, []projectionView{ + {Name: "proj_by_ts", Query: "SELECT ts, id ORDER BY ts"}, + }, views) +} + func TestWebSections_BuildHTMLViewNotFound(t *testing.T) { srv, err := newWebServer(sectionsSchema()) require.NoError(t, err) @@ -247,6 +256,9 @@ func TestWebSections_TablePage(t *testing.T) { assert.Contains(t, body, "Constraints") assert.Contains(t, body, "c_positive") assert.Contains(t, body, "assume") + assert.Contains(t, body, "Projections") + assert.Contains(t, body, "proj_by_ts") + assert.Contains(t, body, "SELECT ts, id ORDER BY ts") } func TestWebSections_MaterializedViewPage(t *testing.T) {