synology_apm_repo.sdk.units.content.saas_artifact module¶
LazyArtifact: the ContentSource implementation shared by
every application-layer artifact this project builds (Calendar’s
.ics, Mail’s .eml, Contact’s CSV) — each cheap to construct a
node for, expensive to actually assemble, and once assembled just a
small, fully in-memory blob of bytes. CalendarProvider/
MailProvider/ContactProvider each supply only the one thing
that’s actually different: the build callback itself.
Teams chat/channel content is the one known exception to “small”:
render_channel_html (saas_teams_chat.py) holds an entire
rendered channel/chat history in memory as one string.
parse_meta_json is a second, smaller shared piece those same
build callbacks (and units/saas/site.py’s own item-_assemble,
one layer up) each need: every one of them starts by parsing a fetched
META object’s raw bytes as JSON before touching its fields.
- synology_apm_repo.sdk.units.content.saas_artifact.parse_meta_json(meta_bytes, label, *, ref=None)¶
Parses
meta_bytesas JSON, raisingDataCorruptError(f"{label} did not parse as JSON: ...", ref=ref)instead of letting a barejson.JSONDecodeErrorpropagate — the identical try/except every META-object JSON parse in this project needs (Mail, Contact, Calendar, SharePoint Site).labelis the caller’s own already-formatted “<item kind> <id!r> META”-shaped prefix, so each keeps its own established wording/ordering rather than this function imposing one.Returns
dict[str, Any]— real META JSON is always an object at the top level, but individual field values are stillAny: every caller immediately narrows them with its own.get()/isinstancechecks (the data is untrusted on-disk content, never assumed-shaped), the same way it already would against a barejson.loads()result.
- class synology_apm_repo.sdk.units.content.saas_artifact.LazyArtifact(build)¶
Bases:
objectImplements
ContentSourcearound anasync build() -> bytescallback, awaited at most once and cached on the firstread/stream. Assembly failures (e.g. a META object shaped in a way the caller doesn’t recognize) surface as whatever exceptionbuilditself raises, the first time any ofread/stream/export_tois actually awaited — constructing aRestorableUnitaround aLazyArtifactnever does I/O or raises on its own.- property size: int | None¶
Noneuntil the artifact has actually been assembled — a synchronous property, but assembling requiresawait, so a caller that needs the real length must first read/stream/export the content.
- property supports_concurrent_export: bool¶
Always
False— an assembled artifact is a single in-memory blob with no bucket concept to spread reads across.
- async read(offset=0, length=None)¶
- async stream(block=8388608)¶
- async export_to(dst, *, sparse=True, progress=None)¶