Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ components:
components_local:
@python3 build/make.py --stack ./data/components_local/index.json

hugo:
# Validate that paths in data/rdi-reference/collections.json all resolve
# to nodes in config.json. Catches drift when the upstream schema changes.
validate_rdi:
@python3 build/validate_rdi_collections.py

hugo: validate_rdi
@hugo $(HUGO_DEBUG) $(HUGO_BUILD)

# json_transform requires hugo to have populated public/ with index.json files
Expand All @@ -33,7 +38,7 @@ ndjson: json_transform
@echo "Compressing NDJSON feed..."
@gzip -kf public/docs.ndjson

serve_hugo:
serve_hugo: validate_rdi
@hugo serve

clean:
Expand Down
86 changes: 86 additions & 0 deletions build/validate_rdi_collections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env python3
"""Validate data/rdi-reference/collections.json against config.json.

Every property path listed in a collection must resolve to a node in the
schema. Use this as a CI check (or run by hand) to catch broken
collections when the schema changes.

Exit code is non-zero if any path is unresolvable, so the script slots
straight into a Makefile target or pre-commit hook.

Usage:
python3 build/validate_rdi_collections.py
"""

from __future__ import annotations

import json
import sys
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[1]
SCHEMA = REPO_ROOT / "data" / "rdi-reference" / "config.json"
COLLECTIONS = REPO_ROOT / "data" / "rdi-reference" / "collections.json"


def collect_paths(node: dict, path: str = "") -> list[str]:
"""Walk the schema the same way the Hugo render-tree partial does.

Steps through `oneOf` (first branch), `patternProperties` (single
wildcard key), and `additionalProperties` (treated as an inline
wrapper) so the path strings match what the rendered page emits.
"""
out: list[str] = []
if not isinstance(node, dict):
return out

inner = node
if "oneOf" in inner:
inner = inner["oneOf"][0]
if "patternProperties" in inner:
inner = next(iter(inner["patternProperties"].values()))
if (
"additionalProperties" in inner
and isinstance(inner["additionalProperties"], dict)
and "properties" not in inner
and "patternProperties" not in inner
):
inner = inner["additionalProperties"]
if "oneOf" in inner:
inner = inner["oneOf"][0]

for name, child in (inner.get("properties") or {}).items():
child_path = f"{path}.{name}" if path else name
out.append(child_path)
out.extend(collect_paths(child, child_path))
return out


def main() -> int:
with SCHEMA.open() as f:
schema = json.load(f)
with COLLECTIONS.open() as f:
collections = json.load(f)

valid = set(collect_paths(schema))

errors: list[str] = []
for entry in collections:
cid = entry.get("id", "<missing id>")
for path in entry.get("properties", []):
if path not in valid:
errors.append(f" [{cid}] unknown path: {path}")

if errors:
print("collections.json references paths that do not exist in config.json:")
print("\n".join(errors))
print(f"\n{len(errors)} broken path(s) across {len(collections)} collection(s).")
return 1

total = sum(len(c.get("properties", [])) for c in collections)
print(f"OK: {total} paths across {len(collections)} collection(s) all resolve.")
return 0


if __name__ == "__main__":
sys.exit(main())
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{{/*
RDI CLI reference content adapter.

Reads the Click-introspection schema in data/rdi-reference/cli.json and
emits one page per command (the root `redis-di` plus each subcommand).

Each emitted page mirrors the frontmatter of the legacy hand-authored
pages in ../cli/ (Title, linkTitle, description, weight, categories).
The body is rendered by `rdi/cli-page-body.html`.
*/}}

{{- $cli := index .Site.Data "rdi-reference" "cli" -}}

{{/* Root page: `redis-di` itself. */}}
{{- $rootBody := partial "rdi/cli-page-body.html" (dict
"cmd" $cli
"name" "redis-di"
) -}}
{{- $.AddPage (dict
"kind" "page"
"path" "redis-di"
"title" "redis-di"
"params" (dict
"linkTitle" "redis-di"
"description" $cli.help
"weight" 10
"alwaysopen" false
"categories" (slice "redis-di")
)
"content" (dict
"mediaType" "text/markdown"
"value" $rootBody
)
) -}}

{{/* One page per subcommand. */}}
{{- range $name, $cmd := $cli.commands -}}
{{- $fullName := printf "redis-di %s" $name -}}
{{- $body := partial "rdi/cli-page-body.html" (dict
"cmd" $cmd
"name" $fullName
) -}}
{{- $.AddPage (dict
"kind" "page"
"path" (printf "redis-di-%s" $name)
"title" $fullName
"params" (dict
"linkTitle" $fullName
"description" $cmd.help
"weight" 10
"alwaysopen" false
"categories" (slice "redis-di")
)
"content" (dict
"mediaType" "text/markdown"
"value" $body
)
) -}}
{{- end -}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
Title: CLI reference (rebuilt from data)
alwaysopen: false
categories:
- docs
- integrate
- rs
- rdi
description: Reference for the RDI CLI commands, rendered from data/rdi-reference/cli.json
group: di
hideListLinks: false
linkTitle: CLI commands (v2)
summary:
Redis Data Integration keeps Redis in sync with the primary database in near
real time.
type: integration
weight: 61
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
Title: Redis Data Integration configuration file (rebuilt from data)
linkTitle: RDI configuration file (v2)
description: Redis Data Integration configuration file reference, rendered from data/rdi-reference/config.json
weight: 11
alwaysopen: false
categories: ["redis-di"]
aliases:
---

{{% rdi-config-reference %}}
Loading
Loading