[ADD] base_dav: CalDAV and CardDAV support (port from 11.0) - #470
Draft
rossigee wants to merge 6 commits into
Draft
[ADD] base_dav: CalDAV and CardDAV support (port from 11.0)#470rossigee wants to merge 6 commits into
rossigee wants to merge 6 commits into
Conversation
Port base_dav from the 11.0 branch, adapting it to Odoo 17.0 and this repo's packaging conventions (pyproject.toml, generated README, tests). Fixes made during the port review: - radicale/collection.py: ItemWrapper was renamed to Item to match the name models/dav_collection.py actually imports; the mismatch caused an ImportError on load. - radicale/auth.py, radicale/collection.py: the ImportError fallbacks for missing radicale set base classes to None, but Auth/Collection/ Storage subclass them directly, which raises TypeError when radicale isn't installed. Fall back to object instead. - radicale/auth.py: use the public request.update_env() instead of setting the private request._env attribute. - __manifest__.py: declare the dateutil external dependency that dav_collection_field_mapping.py already imports.
- dav_get() set result.logger = self.logger for files-type collections, but dav.collection has no logger attribute: any DAV request against a "Files" collection raised AttributeError. - dav_delete() was a no-op for files-type collections, so DELETE requests against a file appeared to succeed without removing the underlying ir.attachment. Implemented deletion mirroring the lookup already used by dav_get(). - from_vobject() only accepted VEVENT components even though get_meta() advertises VTODO and VJOURNAL support too; incoming VTODO/VJOURNAL items returned None which then crashed dav_upload() with an unhandled TypeError. Accept all three component types on import, and raise a clear ValueError from dav_upload() instead of crashing when an item still can't be parsed. Added tests covering the files collection get/delete flow and the vtodo/vjournal/invalid-item from_vobject paths.
… comodel Required Many2one fields default to ondelete='restrict', but ir.model does not support that mode as a comodel, causing registry setup to fail on install.
….fields comodel Same issue as model_id: required Many2one defaults to ondelete='restrict', which ir.model.fields does not support as a comodel.
tools.safe_eval referenced the odoo.tools.safe_eval submodule instead of the safe_eval function within it, breaking domain evaluation and demo data loading. Import safe_eval directly instead. res.users._login requires a user_agent_env argument in Odoo 17; the missing argument raised a TypeError that was silently swallowed by the broad except clause, making DAV authentication always fail.
- Add TestDavCollectionDelete for dav_delete on non-files - Add TestDavRights with 6 authorization test methods - Add TestDavCollectionList for dav_list variants - Add TestRadicaleStorage (5 methods) - Add TestRadicaleCollection (15 methods) - Add TestFieldMappingCode for code-type mappings - Add test_login_raises_exception and test_get_external_login
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports
base_dav(CalDAV/CardDAV access to Odoo data via Radicale) from the 11.0 branch to 17.0, and brings it up to this repo's current packaging conventions.@api.one/@api.multi, noattrs/statesin views).pyproject.toml,readme/fragments (generatedREADME.rstandstatic/description/index.html),development_status/websitemanifest keys, and atests/suite (previously absent).requirements.txtand the addons table inREADME.md.While reviewing the port I found and fixed a few real bugs, not just formatting:
radicale/collection.py:models/dav_collection.pyimports a class namedItem, but the module definedItemWrapper— anImportErroron load. Renamed toItem.radicale/auth.py,radicale/collection.py: theImportErrorfallback for a missingradicalepackage set the base classes toNone, butAuth/Collection/Storagesubclass them directly, which raisesTypeErrorat import time. Fall back toobjectinstead so the addon can at least install withoutradicalepresent (matching the controller's existing "radicale not installed" runtime message).radicale/auth.py: switched from writing the privaterequest._envattribute to the publicrequest.update_env(user=uid)API.__manifest__.py: declared thedateutilexternal dependency thatdav_collection_field_mapping.pyalready imports.dav_get()setresult.logger = self.loggerforfiles-type collections, butdav.collectionhas nologgerattribute — any DAV request against a "Files" collection raisedAttributeError. Removed.dav_delete()was a no-op forfiles-type collections, so DELETE requests against a file appeared to succeed without removing the underlyingir.attachment. Implemented deletion mirroring the lookup already used bydav_get().from_vobject()only acceptedVEVENTcomponents even thoughget_meta()advertisesVTODO/VJOURNALsupport too; an incomingVTODO/VJOURNALitem returnedNone, which then crasheddav_upload()with an unhandledTypeError. Now accepts all three component types on import, anddav_upload()raises a clearValueErrorinstead of crashing when an item still can't be parsed.Marked as draft because the new tests, while written against the actual code paths (including
Auth._login), have not yet actually been executed — no Odoo 17 runtime orradicale/vobjectpackages were available in the environment where this was prepared, so this only got a static lint/pre-commit pass, not a real test run.Known limitations
These were identified during review but intentionally not touched in this PR, because implementing them correctly requires verifying against Radicale's actual internal API contract (return types, expected exceptions, calling conventions), which isn't possible without a live
radicaleinstall to test against — guessing at the shape risks landing plausible-looking but silently-wrong code:dav_uploadfordav_type == "files") always returnsNone— no file upload support via WebDAV for "Files" collections, only listing/download/delete.Storage.create_collection()andStorage.move()inradicale/collection.pyare no-ops — a DAV client's native "create calendar" or "move/rename item" actions won't do anything; collections can only be created through the Odoo backend UI.Collection.set_meta()is a no-op — a client renaming a calendar or changing its color viaPROPPATCHwon't have it persisted.Storage.acquire_lock()yields without acquiring anything — no protection against concurrent writes from multiple DAV clients.sync()raises if a client passes back a previous sync token, sosync-collectionREPORT (used by most modern CalDAV/CardDAV clients for efficient incremental sync) always falls back to a full resync.to_vobject()always exportsVEVENT; VTODO/VJOURNAL export isn't modeled (there's no per-collection "component type" field), even though import now accepts them.Test plan
tests/test_dav_collection.pyviaodoo --test-enable -i base_dav -d <db>in an Odoo 17.0 environment withradicale,vobject,python-dateutilpip-installed (a single Odoo test DB is all that's needed — Radicale itself is stateless and stores nothing of its own; it delegates entirely to thedav.collectionOdoo model via ourStorage/Collectionglue classes)/.dav/{username}/{collection_id}/) against the demo collectionspre-commit runclean (ruff, ruff-format, pylint_odoo, oca-checks-odoo-module, prettier, oca-gen-addon-readme, oca-gen-external-dependencies)