feat(inbox): root frontmatter emission at Door B with a caller-owned bundle_id
This commit is contained in:
parent
cd2c7517b0
commit
224121f762
2 changed files with 214 additions and 2 deletions
|
|
@ -21,13 +21,14 @@ from __future__ import annotations
|
|||
|
||||
import hashlib
|
||||
import unicodedata
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Callable, Mapping
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from .errors import IngestError, MaterializationError, SourceError
|
||||
from .extract import extract_text
|
||||
from .materialize import (
|
||||
_render_root_frontmatter,
|
||||
check_filename_length,
|
||||
link_in_index,
|
||||
parse_frontmatter,
|
||||
|
|
@ -244,6 +245,7 @@ def process_inbox(
|
|||
okf_type: str,
|
||||
gate: Gate,
|
||||
profile: BundleProfile = DEFAULT,
|
||||
root_frontmatter_values: Mapping[str, str] | None = None,
|
||||
) -> InboxResult:
|
||||
"""Convert every file dropped in `inbox_dir` into an OKF concept.
|
||||
|
||||
|
|
@ -261,6 +263,12 @@ def process_inbox(
|
|||
a reserved `okf_type`, and a missing inbox directory.
|
||||
"""
|
||||
validate_ingested_at(ingested_at)
|
||||
# Rendered HERE, before anything is read or written, and the result carried
|
||||
# to the index write at the bottom. `_render_root_frontmatter` refuses a key
|
||||
# the policy does not name, and a refusal must leave no bundle behind --
|
||||
# `materialize.py` states the same rule for Door A, and a door that half-built
|
||||
# a bundle before refusing would be worse than one that never started.
|
||||
root_head = _render_root_frontmatter(root_frontmatter_values or {}, profile=profile)
|
||||
run_rejection = profile.types.rejection(okf_type)
|
||||
if run_rejection is not None:
|
||||
raise MaterializationError(f"okf_type {run_rejection.reason}", code=run_rejection.code)
|
||||
|
|
@ -398,7 +406,9 @@ def process_inbox(
|
|||
if persisted:
|
||||
index_path = bundle / profile.index.name
|
||||
if not index_path.is_file():
|
||||
write_bytes(bundle, profile.index.name, "")
|
||||
write_bytes(bundle, profile.index.name, root_head)
|
||||
else:
|
||||
_refresh_root_frontmatter(index_path, root_head)
|
||||
if profile.index.facets is None:
|
||||
for entry in persisted:
|
||||
link_in_index(
|
||||
|
|
@ -436,6 +446,36 @@ def _validate_facets(structure: DocumentStructure, profile: BundleProfile) -> No
|
|||
raise MaterializationError(str(exc), code="index_facet_invalid") from exc
|
||||
|
||||
|
||||
def _refresh_root_frontmatter(index_path: Path, head: str) -> None:
|
||||
"""Put `head` at the top of an existing index, replacing any block already there.
|
||||
|
||||
Idempotent by construction: the leading block is removed and the current one
|
||||
written, so applying this twice produces the same bytes. That is what makes a
|
||||
second round with the same `bundle_id` a no-op and a rebuild-from-scratch
|
||||
byte-identical to an incremental update -- neither is diffed against the
|
||||
other, both are the same function of the same inputs.
|
||||
|
||||
The block is recognised the way `parse_frontmatter` recognises one: an
|
||||
opening `---` on the very first line, up to the next `---`. Nothing else is
|
||||
read, because a value inside the block is the caller's and this door only
|
||||
ever restates it.
|
||||
"""
|
||||
body = index_path.read_bytes().decode("utf-8")
|
||||
lines = body.splitlines(keepends=True)
|
||||
if lines and lines[0].strip() == "---":
|
||||
for position, line in enumerate(lines[1:], start=1):
|
||||
if line.strip() == "---":
|
||||
rest = lines[position + 1 :]
|
||||
# The blank line the block is separated by belongs to the block.
|
||||
if rest and rest[0].strip() == "":
|
||||
rest = rest[1:]
|
||||
body = "".join(rest)
|
||||
break
|
||||
if body == head:
|
||||
return
|
||||
index_path.write_bytes((head + body).encode("utf-8"))
|
||||
|
||||
|
||||
def _reproject_index(bundle: Path, profile: BundleProfile) -> None:
|
||||
"""Rewrite the managed region of the index from the WHOLE bundle.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue