synology_apm_repo.sdk.units.saas.services module

Content inspection for embedded saas_obj objects.

The SaaS application layer locates a service-level DB snapshot via the connector’s own object-name index (see object_name_index.py) — a top-down lookup into fixed, connector-written bookkeeping, never a guess. This module’s role is different: given an already-located object’s raw bytes, determine (sniff) or read (inspect_object) what it is — the corruption check every object-name-index caller still wants (confirm it decompresses, confirm it’s really SQLite, confirm it defines the expected table), never discovery of an unknown object’s location; every provider locates objects by direct index lookup because a schema-only scan couldn’t reliably tell apart schema-identical tables (Archive Mail’s mail_table from regular Mail’s, say).

Raw bytes classify into one of ServiceKind’s members by a fixed prefix rule: ZSTD-framed SQLite, then a JSON object, then an RFC822 header, else binary. (Real SnapshotDB reassembly is unavailable offline.)

class synology_apm_repo.sdk.units.saas.services.ServiceKind(value)

Bases: Enum

What one already-located object’s raw bytes turn out to be (sniff’s classification).

  • SERVICE_DB: ZSTD -> SQLite; the owning app is guessed from its sqlite_master table names (_SERVICE_TABLE_HINTS).

  • INDEX: JSON matching the db_objects/ db_infos_in_snapshot shape (_index_entries) — extracted as IndexEntry tuples.

  • META_JSON: any other JSON object (content-list fragments, Contact/Calendar client metadata, search-index documents).

  • MAIL_SKELETON: an RFC822 header in the first few hundred bytes.

  • BINARY: anything else — the common case for real Drive content.

INDEX = 'index'
SERVICE_DB = 'service_db'
META_JSON = 'meta_json'
MAIL_SKELETON = 'mail_skeleton'
BINARY = 'binary'
class synology_apm_repo.sdk.units.saas.services.IndexEntry(name, object_id)

Bases: object

One entry in a connector’s own index object: a display name paired with the backing object id.

name: str
object_id: str
class synology_apm_repo.sdk.units.saas.services.SniffResult(kind, tables=frozenset({}), service_name=None, index_entries=())

Bases: object

What sniffing a service-DB/index object’s bytes found: its ServiceKind, plus whichever of tables/service_name/ index_entries that kind actually carries.

kind: ServiceKind
tables: frozenset[str] = frozenset({})
service_name: str | None = None
index_entries: tuple[IndexEntry, ...] = ()
async synology_apm_repo.sdk.units.saas.services.decompress_service_db(data)

Decompress one service-level DB snapshot’s raw bytes to plain SQLite bytes — the half of open_service_db that callers holding bytes directly need (TeamsChatProvider’s several short-lived connections).

Deliberately unbounded, unlike sniff’s capped speculative read: content here is already identified via the object-name index, so a resolved-but-wrong location is caught by the schema check every caller already does afterward, not by refusing to decompress upfront. Also deliberately unbounded in size – large real service DBs are expected – which is exactly why this is async and hops to a real OS thread for the decrypt+decompress itself: peel() itself is pure, synchronous bytes work with no I/O, but leaving a large decrypt+decompress on the event loop would stall every other Task (TUI redraws, a progress callback, a concurrent metadata read) for its duration — the same responsiveness reasoning behind dedup/pool/_bucket_reader.py’s own per-chunk-vs-per-run thread-hop split.

Raises:

DataCorruptError – data isn’t ZSTD-framed SQLite.

async synology_apm_repo.sdk.units.saas.services.open_service_db(data)

Decompress and open one service-level DB snapshot’s raw bytes as a live, queryable connection — the counterpart to sniff for callers (the application-layer providers) that need to actually run queries against the DB, not just inspect it.

async synology_apm_repo.sdk.units.saas.services.sniff(data)

Classify one object’s raw bytes into one of ServiceKind’s shapes. Pure function of data — touches no repository, no store, no DedupFile — so it’s cheap to unit test with synthetic bytes and is reused as-is by inspect_object; it is async because the SERVICE_DB branch opens the decompressed payload as real SQLite to read its table names (that’s I/O), and because the speculative decompress attempt below hops to a real OS thread rather than blocking the event loop — up to _MAX_SNIFF_DECOMPRESS (128 MiB) of synchronous work otherwise, on data this function hasn’t even confirmed is real SQLite yet. Never raises: a peel failure here means “not actually zstd-framed”, not “sniff itself failed”.

async synology_apm_repo.sdk.units.saas.services.inspect_object(dedup_file, offset, length)

Read just enough of an already-located object’s bytes to classify (sniff) and validate it — never a step in finding the object: offset/length always come from the connector’s own object-name index or from an already-resolved INDEX object’s own entries. Applies _FULL_READ_CAP (magic-gated, not a blind size cutoff).