From dd28dd8ba4b243ac69ab32a94fb4e149914f8d28 Mon Sep 17 00:00:00 2001 From: Joscha Feth Date: Tue, 15 Jul 2025 13:56:32 +0100 Subject: [PATCH 1/3] feat: add DeletedUserObject and update user references in page properties --- .../notion/models/objects/properties/page_property.py | 6 +++--- pydantic_api/notion/models/objects/user.py | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pydantic_api/notion/models/objects/properties/page_property.py b/pydantic_api/notion/models/objects/properties/page_property.py index 0c07ed5..7438c90 100644 --- a/pydantic_api/notion/models/objects/properties/page_property.py +++ b/pydantic_api/notion/models/objects/properties/page_property.py @@ -9,7 +9,7 @@ from pydantic import AnyUrl, Field, EmailStr from pydantic_api.base import BaseModel -from ..user import UserObject, UserObjectFactory +from ..user import DeletedUserObject, UserObject, UserObjectFactory from ..file import FileObject, FileObjectFactory from ..block import RichTextObject, RichTextObjectFactory from .common import ( @@ -55,7 +55,7 @@ def new( # created_by, Refer to https://developers.notion.com/reference/page-property-values#created_by class CreatedByProperty(BasePageProperty): type: Literal["created_by"] = "created_by" - created_by: UserObject + created_by: Union[UserObject, DeletedUserObject] @classmethod def new_from_person( @@ -192,7 +192,7 @@ class FormulaProperty(BasePageProperty): # last_edited_by: Refer to https://developers.notion.com/reference/page-property-values#last_edited_by class LastEditedByProperty(BasePageProperty): type: Literal["last_edited_by"] = "last_edited_by" - last_edited_by: UserObject + last_edited_by: Union[UserObject, DeletedUserObject] @classmethod def new_from_person( diff --git a/pydantic_api/notion/models/objects/user.py b/pydantic_api/notion/models/objects/user.py index 4ade108..8a63d57 100644 --- a/pydantic_api/notion/models/objects/user.py +++ b/pydantic_api/notion/models/objects/user.py @@ -102,6 +102,11 @@ class BotUserObject(_BaseUserObject): ) +class DeletedUserObject(PartialUser): + """A deleted user object.""" + pass + + UserObject = Annotated[ Union[PersonUserObject, BotUserObject], Field(discriminator="type") ] From b7e30f0fae7012dd469ee55a59837f245fd2bcc9 Mon Sep 17 00:00:00 2001 From: Joscha Feth Date: Tue, 15 Jul 2025 13:57:04 +0100 Subject: [PATCH 2/3] test: add testing capabilities with pytest and update dependencies in poetry.lock --- devenv.nix | 4 ++ poetry.lock | 132 ++++++++++++++++++++++++++++++++++++-- pyproject.toml | 1 + tests/test_user_object.py | 18 ++++++ 4 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 tests/test_user_object.py diff --git a/devenv.nix b/devenv.nix index 0b9820a..8706633 100644 --- a/devenv.nix +++ b/devenv.nix @@ -40,4 +40,8 @@ scripts.format.exec = '' pre-commit run --all-files ''; + + enterTest = '' + poetry run pytest + ''; } diff --git a/poetry.lock b/poetry.lock index 4134bac..9e61512 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "annotated-types" @@ -6,17 +6,32 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + [[package]] name = "dnspython" version = "2.6.1" description = "DNS toolkit" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, @@ -37,6 +52,7 @@ version = "2.2.0" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, @@ -52,6 +68,7 @@ version = "2.14.0" description = "Emoji for Python" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "emoji-2.14.0-py3-none-any.whl", hash = "sha256:fcc936bf374b1aec67dda5303ae99710ba88cc9cdce2d1a71c5f2204e6d78799"}, {file = "emoji-2.14.0.tar.gz", hash = "sha256:f68ac28915a2221667cddb3e6c589303c3c6954c6c5af6fefaec7f9bdf72fdca"}, @@ -60,12 +77,32 @@ files = [ [package.extras] dev = ["coverage", "pytest (>=7.4.4)"] +[[package]] +name = "exceptiongroup" +version = "1.3.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version == \"3.10\"" +files = [ + {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, + {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "idna" version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -74,12 +111,25 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + [[package]] name = "mypy" version = "1.15.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, @@ -133,17 +183,47 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + [[package]] name = "pydantic" version = "2.9.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, @@ -160,7 +240,7 @@ typing-extensions = [ [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] +timezone = ["tzdata ; python_version >= \"3.9\" and sys_platform == \"win32\""] [[package]] name = "pydantic-api-models" @@ -168,6 +248,7 @@ version = "0.0.2" description = "Make API self-explanatory with pydantic" optional = false python-versions = "<4.0,>=3.8" +groups = ["main"] files = [ {file = "pydantic_api_models-0.0.2-py3-none-any.whl", hash = "sha256:cb5bbb7177e51a2022e4ffb503f5818a5549238e3441ad5054199f5d55d7bd56"}, {file = "pydantic_api_models-0.0.2.tar.gz", hash = "sha256:917c13220c06bd9239f28579fdb636364b5ec80c12aaa8009e17cbd5581a2332"}, @@ -182,6 +263,7 @@ version = "2.23.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, @@ -277,12 +359,53 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "8.4.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, + {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + [[package]] name = "tomli" version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -324,12 +447,13 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.10" -content-hash = "309a4a4edb68cc72a98105f4c05300a41a372ea0e2c32d2bd2adbc5d9d5c319e" +content-hash = "6e6b33a56f1a116702ee5caebe9dde0335d2c82a37e886bb192bee575702448c" diff --git a/pyproject.toml b/pyproject.toml index 1d9f758..b3d05bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ pydantic-api-models = "^0.0.2" [tool.poetry.group.dev.dependencies] mypy = "^1.15.0" +pytest = "^8.0.0" [build-system] requires = ["poetry-core>=1.5.0"] diff --git a/tests/test_user_object.py b/tests/test_user_object.py new file mode 100644 index 0000000..29ef9ef --- /dev/null +++ b/tests/test_user_object.py @@ -0,0 +1,18 @@ +import uuid +from pydantic_api.notion.models.objects.properties.page_property import CreatedByProperty +from pydantic_api.notion.models.objects.user import UserObject, DeletedUserObject + + +def test_userobject_accepts_deleteduser(): + user_id = uuid.uuid4() + data = { + "type": "created_by", + "created_by": { + "object": "user", + "id": user_id, + } + } + created_by = CreatedByProperty.model_validate(data) + assert isinstance(created_by.created_by, DeletedUserObject) + assert created_by.created_by.object == "user" + assert created_by.created_by.id == user_id From 6e7a599754a0c79d7e5f51841b912195804a80ad Mon Sep 17 00:00:00 2001 From: Joscha Feth Date: Tue, 15 Jul 2025 14:14:03 +0100 Subject: [PATCH 3/3] test: add __init__.py for test module initialization --- tests/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29