angee.knowledge.schema
Strawberry-Django schema contributions for the knowledge addon.
VaultType
@strawberry_django.type(Vault)
class VaultType(AngeeNode)GraphQL projection of a vault.
owner
@strawberry_django.field(only=["owner_id"])
def owner() -> strawberry.ID | NoneReturn the owner's public id without exposing the user object.
owner_label
@strawberry_django.field(only=["owner_id"])
def owner_label(info: strawberry.Info) -> str | NoneReturn the owner's display label — no user object exposed.
OutlineEntryType
@strawberry.type
class OutlineEntryType()One ATX heading in a page body's outline.
MarkdownPageType
@strawberry_django.type(MarkdownPage)
class MarkdownPageType(AngeeNode)GraphQL projection of a page's markdown body sidecar.
page
@strawberry_django.field(only=["page_id"])
def page() -> strawberry.IDReturn the owning page's public id.
excerpt
@strawberry_django.field(only=["body"])
def excerpt() -> strReturn the leading body characters used for list previews.
outline
@strawberry_django.field(only=["body"])
def outline() -> list[OutlineEntryType]Return the body's heading outline, derived like :attr:excerpt.
Parsed through the body's own structure owner (:meth:MarkdownPage.parse_outline) so the read field and the section-patch write share one markdown parser.
BacklinkType
@strawberry.type
class BacklinkType()One resolved page that links to the page being viewed.
PageType
@strawberry_django.type(Page)
class PageType(AuthoredRefMixin, AngeeNode)GraphQL projection of a page.
vault
@strawberry_django.field(only=["vault_id"])
def vault() -> strawberry.IDReturn the owning vault's public id without exposing the vault.
vault_label
@strawberry_django.field(only=["vault_id"])
def vault_label() -> str | NoneReturn the owning vault's display name — no vault object exposed.
Resolved under system_context so a page viewer who lacks vault read still sees where the page lives; only the name string leaves the resolver.
parent
@strawberry_django.field(only=["parent_id"])
def parent() -> strawberry.ID | NoneReturn the parent page's public id, if the page has one.
markdown
@strawberry_django.field(only=["id"])
def markdown() -> MarkdownPageType | NoneReturn the markdown body sidecar visible to the actor, if any.
backlinks
@strawberry_django.field(only=["id"])
def backlinks() -> list[BacklinkType]Return resolved pages linking here, scoped to readable sources.
The source title is annotated across the relation rather than select_related-ed: source_page is REBAC-guarded, so materializing it inside an actor-scoped resolver would fail.
PageBodyPayload
@strawberry.type
class PageBodyPayload()Result of a markdown body write.
SectionOp
@strawberry.enum
class SectionOp(Enum)How :meth:patch_page_section splices content into a section.
The member value is the op token the markdown owner (:meth:MarkdownPage.spliced_section) accepts; the upper-case member name is the wire enum value.
VaultWriteBackend
class VaultWriteBackend(AngeeHasuraWriteBackend)Write semantics for vaults: create belongs to the manager factory.
create
def create(info: strawberry.Info, data: dict[str, Any]) -> AnyCreate a vault owned by the requesting user.
PageWriteBackend
class PageWriteBackend(AngeeHasuraWriteBackend)Write semantics for pages: create belongs to the manager factory.
create
def create(info: strawberry.Info, data: dict[str, Any]) -> AnyCreate a page in a vault the requesting user can write.
MAX_SEARCH_PAGE_SIZE
Upper bound on :meth:KnowledgeQuery.search_pages first — every backend inherits it.
KnowledgeQuery
@strawberry.type
class KnowledgeQuery()Knowledge content queries that span the page/body join.
search_pages
@strawberry.field
def search_pages(vault: PublicID,
query: str,
first: int = 20) -> list[PageType]Return actor-visible pages in vault matching query.
The vault is both the search namespace and the selection point: this resolves it (gating the actor's read), then delegates to its bound :class:~angee.knowledge.retrieval.RetrievalBackend (default lexical), so a semantic plugin can swap the strategy without editing this resolver. Row scope is the backend's responsibility (apply_ambient_scope).
first is clamped here so every backend inherits the bound. The result is a materialized list, so a nested markdown/backlinks/vault_label selection runs a per-page resolver — a bounded N+1 accepted now that first is capped (a dataloader is the future optimization, not v1's concern).
KnowledgeMutation
@strawberry.type
class KnowledgeMutation()Markdown body writes that belong to the Knowledge domain.
delete_vault
@strawberry.mutation(name="delete_vault")
def delete_vault(id: PublicID, confirm: bool = False) -> DeletePreviewReturn or apply the authored vault cascade delete preview.
delete_page
@strawberry.mutation(name="delete_page")
def delete_page(id: PublicID, confirm: bool = False) -> DeletePreviewReturn or apply the authored page cascade delete preview.
update_page_body
@strawberry.mutation(name="update_page_body")
def update_page_body(
page: PublicID,
body: str,
expected_hash: Annotated[
str | None,
strawberry.argument(name="expected_hash"),
] = None
) -> PageBodyPayloadWrite a page's markdown body, last-write-wins with a stale guard.
patch_page_section
@strawberry.mutation(name="patch_page_section")
def patch_page_section(
page: PublicID,
heading_path: list[str],
op: SectionOp,
content: str,
expected_hash: Annotated[
str | None,
strawberry.argument(name="expected_hash"),
] = None
) -> PageBodyPayloadReplace/append/prepend the section at heading_path in a page body.
Mirrors :meth:update_page_body: same PublicID resolution, same :class:PageBodyPayload, same CAS via expected_hash; the splice is a fail-fast structured edit (SECTION_NOT_FOUND/AMBIGUOUS_MATCH).
replace_page_text
@strawberry.mutation(name="replace_page_text")
def replace_page_text(
page: PublicID,
old: str,
new: str,
expected_hash: Annotated[
str | None,
strawberry.argument(name="expected_hash"),
] = None
) -> PageBodyPayloadReplace the single occurrence of old with new in a page body.
Mirrors :meth:update_page_body; uniqueness is enforced by the markdown owner, so a non-unique or absent target fails fast with AMBIGUOUS_MATCH/SECTION_NOT_FOUND before any write.
append_to_page
@strawberry.mutation(name="append_to_page")
def append_to_page(
page: PublicID,
content: str,
expected_hash: Annotated[
str | None,
strawberry.argument(name="expected_hash"),
] = None
) -> PageBodyPayloadAppend content to the end of a page body.
Mirrors :meth:update_page_body: same PublicID resolution, same :class:PageBodyPayload, same CAS via expected_hash; the markdown owner joins content one blank line after the body (no markdown re-rendered), so the section seam stays consistent.