formfeed 0.3.2__tar.gz → 0.3.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: formfeed
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Formfeed API client: template-based PDF and image generation (sync and async, httpx).
5
5
  Project-URL: Homepage, https://formfeed.dev
6
6
  Project-URL: Documentation, https://docs.formfeed.dev/api/sdks/python
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "formfeed"
7
- version = "0.3.2"
7
+ version = "0.3.4"
8
8
  description = "Formfeed API client: template-based PDF and image generation (sync and async, httpx)."
9
9
  readme = "README.md"
10
10
  # PEP 639: the SPDX expression replaces the licence classifier, the two must not be combined
@@ -62,4 +62,4 @@ __all__ = [
62
62
  "verify_webhook_signature",
63
63
  ]
64
64
 
65
- __version__ = "0.3.2"
65
+ __version__ = "0.3.4"
@@ -499,6 +499,11 @@ def _guard(allow_breaking: bool) -> dict[str, Any] | None:
499
499
  return {"allow_breaking": True} if allow_breaking else None
500
500
 
501
501
 
502
+ def _schema_path(id_or_slug: str, version: str | int | None) -> str:
503
+ params = httpx.QueryParams(_query(version=None if version is None else str(version)))
504
+ return f"/templates/{id_or_slug}/schema{'?' + str(params) if params else ''}"
505
+
506
+
502
507
  class _Channels:
503
508
  """Release channels: a render with ``version="staging"`` uses the version the channel points at.
504
509
  Moves reach renders within a minute; a breaking data schema change raises ``schema_breaking_change``
@@ -587,8 +592,9 @@ class _Templates:
587
592
  """Publishes a version; a data schema that breaks callers of the published one raises ``schema_breaking_change`` unless ``allow_breaking``."""
588
593
  return TemplateVersion.model_validate(self._c.request("POST", f"/templates/{id_or_slug}/versions/{number}/publish", _guard(allow_breaking)))
589
594
 
590
- def schema(self, id_or_slug: str) -> dict[str, Any]:
591
- return self._c.request("GET", f"/templates/{id_or_slug}/schema")
595
+ def schema(self, id_or_slug: str, *, version: str | int | None = None) -> dict[str, Any]:
596
+ """The JSON Schema of the template's data, stored or inferred from the sample data: of the published (else the latest) version, or of ``version`` (``published``, ``latest``, a number or a channel name)."""
597
+ return self._c.request("GET", _schema_path(id_or_slug, version))
592
598
 
593
599
  def validate(self, id_or_slug: str, data: dict[str, Any] | None = None, *, version: str | int = "published") -> TemplateValidation:
594
600
  """Checks a version with data without rendering. Free. Without ``data``, the version's sample data is checked."""
@@ -971,8 +977,8 @@ class _AsyncTemplates:
971
977
  async def publish(self, id_or_slug: str, number: int, *, allow_breaking: bool = False) -> TemplateVersion:
972
978
  return TemplateVersion.model_validate(await self._c.request("POST", f"/templates/{id_or_slug}/versions/{number}/publish", _guard(allow_breaking)))
973
979
 
974
- async def schema(self, id_or_slug: str) -> dict[str, Any]:
975
- return await self._c.request("GET", f"/templates/{id_or_slug}/schema")
980
+ async def schema(self, id_or_slug: str, *, version: str | int | None = None) -> dict[str, Any]:
981
+ return await self._c.request("GET", _schema_path(id_or_slug, version))
976
982
 
977
983
  async def validate(self, id_or_slug: str, data: dict[str, Any] | None = None, *, version: str | int = "published") -> TemplateValidation:
978
984
  body: dict[str, Any] = {"version": version}
@@ -341,6 +341,10 @@ class Diagnostic(_Model):
341
341
  location: dict[str, int] | None = None
342
342
  path: str | None = None
343
343
  """Data errors only: where in the data, e.g. ``data.invoice.lines[0].qty``."""
344
+ part: str | None = None
345
+ """Word and PowerPoint templates only: the file part the tag is in, e.g. ``word/document.xml``."""
346
+ paragraph: int | None = None
347
+ """Word and PowerPoint templates only: the 1-based paragraph in ``part``."""
344
348
 
345
349
 
346
350
  class TemplateValidation(_Model):
@@ -313,6 +313,19 @@ def test_templates_validate_sends_version_and_data():
313
313
  assert checked.ok is False and checked.diagnostics[0].path == "data.n" and checked.template.version == 3
314
314
  assert str(rec.calls[0].url).endswith("/templates/invoice/validate")
315
315
  assert json.loads(rec.calls[0].content) == {"version": "latest", "data": {"a": 1}}
316
+ # a channel name names its main version, on the check and on the schema
317
+ client.templates.validate("invoice", version="staging")
318
+ assert json.loads(rec.calls[1].content) == {"version": "staging"}
319
+
320
+
321
+ def test_templates_schema_of_a_version_or_channel():
322
+ client, rec = sync_client(lambda req, n: _json({"type": "object"}))
323
+ assert client.templates.schema("invoice") == {"type": "object"}
324
+ assert str(rec.calls[0].url).endswith("/templates/invoice/schema")
325
+ client.templates.schema("invoice", version="staging")
326
+ assert str(rec.calls[1].url).endswith("/templates/invoice/schema?version=staging")
327
+ client.templates.schema("invoice", version=8)
328
+ assert str(rec.calls[2].url).endswith("/templates/invoice/schema?version=8")
316
329
 
317
330
 
318
331
  def test_library_files_as_image_watermarks_and_merge_sources():
File without changes
File without changes
File without changes
File without changes