mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-05 22:02:39 +02:00
feat: refine Obsidian plugin routes and schemas for improved device management and API stability
This commit is contained in:
parent
60d9e7ed8c
commit
b5c9388c8a
9 changed files with 182 additions and 385 deletions
|
|
@ -1,23 +1,8 @@
|
|||
"""
|
||||
Obsidian Plugin connector schemas.
|
||||
"""Wire schemas spoken between the SurfSense Obsidian plugin and the backend.
|
||||
|
||||
Wire format spoken between the SurfSense Obsidian plugin
|
||||
(``surfsense_obsidian/``) and the FastAPI backend.
|
||||
|
||||
Stability contract
|
||||
------------------
|
||||
Every request and response schema sets ``model_config = ConfigDict(extra='ignore')``.
|
||||
This is the API stability contract — not just hygiene:
|
||||
|
||||
- Old plugins talking to a newer backend silently drop any new response fields
|
||||
they don't understand instead of failing validation.
|
||||
- New plugins talking to an older backend can include forward-looking request
|
||||
fields (e.g. attachments metadata) without the older backend rejecting them.
|
||||
|
||||
Hard breaking changes are reserved for the URL prefix (``/api/v2/...``).
|
||||
Additive evolution is signaled via the ``capabilities`` array on
|
||||
``HealthResponse`` / ``ConnectResponse`` — older plugins ignore unknown
|
||||
capability strings safely.
|
||||
All schemas inherit ``extra='ignore'`` from :class:`_PluginBase` so additive
|
||||
field changes never break either side; hard breaks live behind a new URL
|
||||
prefix (``/api/v2/...``).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -31,22 +16,13 @@ _PLUGIN_MODEL_CONFIG = ConfigDict(extra="ignore")
|
|||
|
||||
|
||||
class _PluginBase(BaseModel):
|
||||
"""Base class for all plugin payload schemas.
|
||||
|
||||
Carries the forward-compatibility config so subclasses don't have to
|
||||
repeat it.
|
||||
"""
|
||||
"""Base schema carrying the shared forward-compatibility config."""
|
||||
|
||||
model_config = _PLUGIN_MODEL_CONFIG
|
||||
|
||||
|
||||
class NotePayload(_PluginBase):
|
||||
"""One Obsidian note as pushed by the plugin.
|
||||
|
||||
The plugin is the source of truth: ``content`` is the post-frontmatter
|
||||
body, ``frontmatter``/``tags``/``headings``/etc. are precomputed by the
|
||||
plugin via ``app.metadataCache`` so the backend doesn't have to re-parse.
|
||||
"""
|
||||
"""One Obsidian note as pushed by the plugin (the source of truth)."""
|
||||
|
||||
vault_id: str = Field(..., description="Stable plugin-generated UUID for this vault")
|
||||
path: str = Field(..., description="Vault-relative path, e.g. 'notes/foo.md'")
|
||||
|
|
@ -68,7 +44,7 @@ class NotePayload(_PluginBase):
|
|||
|
||||
|
||||
class SyncBatchRequest(_PluginBase):
|
||||
"""Batch upsert. Plugin sends 10-20 notes per request to amortize HTTP overhead."""
|
||||
"""Batch upsert; plugin sends 10-20 notes per request."""
|
||||
|
||||
vault_id: str
|
||||
notes: list[NotePayload] = Field(default_factory=list, max_length=100)
|
||||
|
|
@ -90,8 +66,6 @@ class DeleteBatchRequest(_PluginBase):
|
|||
|
||||
|
||||
class ManifestEntry(_PluginBase):
|
||||
"""One row of the server-side manifest used by the plugin to reconcile."""
|
||||
|
||||
hash: str
|
||||
mtime: datetime
|
||||
|
||||
|
|
@ -104,26 +78,18 @@ class ManifestResponse(_PluginBase):
|
|||
|
||||
|
||||
class ConnectRequest(_PluginBase):
|
||||
"""First-call handshake to register or look up a vault connector row."""
|
||||
"""Vault registration / heartbeat. Replayed on every plugin onload."""
|
||||
|
||||
vault_id: str
|
||||
vault_name: str
|
||||
search_space_id: int
|
||||
plugin_version: str
|
||||
device_id: str
|
||||
device_label: str | None = Field(
|
||||
default=None,
|
||||
description="User-friendly device name shown in the web UI (e.g. 'iPad Pro').",
|
||||
)
|
||||
|
||||
|
||||
class ConnectResponse(_PluginBase):
|
||||
"""Returned from POST /connect.
|
||||
|
||||
Carries the same handshake fields as ``HealthResponse`` so the plugin
|
||||
learns the contract on its very first call without an extra round-trip
|
||||
to ``GET /health``.
|
||||
"""
|
||||
"""Carries the same handshake fields as ``HealthResponse`` so the plugin
|
||||
learns the contract without a separate ``GET /health`` round-trip."""
|
||||
|
||||
connector_id: int
|
||||
vault_id: str
|
||||
|
|
@ -133,14 +99,7 @@ class ConnectResponse(_PluginBase):
|
|||
|
||||
|
||||
class HealthResponse(_PluginBase):
|
||||
"""API contract handshake.
|
||||
|
||||
The plugin calls ``GET /health`` once per ``onload`` and caches the
|
||||
result. ``capabilities`` is a forward-extensible string list: future
|
||||
additions (``'pat_auth'``, ``'scoped_pat'``, ``'attachments_v2'``,
|
||||
``'shared_search_spaces'``...) ship without breaking older plugins
|
||||
because they only enable extra behavior, never gate existing endpoints.
|
||||
"""
|
||||
"""API contract handshake. ``capabilities`` is additive-only string list."""
|
||||
|
||||
api_version: str
|
||||
capabilities: list[str]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue