__init__.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import json
  2. from enum import Enum
  3. from typing import Sequence
  4. from radicale import pathutils, utils
  5. from radicale.log import logger
  6. INTERNAL_TYPES: Sequence[str] = ("none", "rabbitmq", "email")
  7. def load(configuration):
  8. """Load the storage module chosen in configuration."""
  9. try:
  10. return utils.load_plugin(
  11. INTERNAL_TYPES, "hook", "Hook", BaseHook, configuration)
  12. except Exception as e:
  13. logger.warning(e)
  14. logger.warning("Hook \"%s\" failed to load, falling back to \"none\"." % configuration.get("hook", "type"))
  15. configuration = configuration.copy()
  16. configuration.update({"hook": {"type": "none"}}, "hook", privileged=True)
  17. return utils.load_plugin(
  18. INTERNAL_TYPES, "hook", "Hook", BaseHook, configuration)
  19. class BaseHook:
  20. def __init__(self, configuration):
  21. """Initialize BaseHook.
  22. ``configuration`` see ``radicale.config`` module.
  23. The ``configuration`` must not change during the lifetime of
  24. this object, it is kept as an internal reference.
  25. """
  26. self.configuration = configuration
  27. def notify(self, notification_item):
  28. """Upload a new or replace an existing item."""
  29. raise NotImplementedError
  30. class HookNotificationItemTypes(Enum):
  31. CPATCH = "cpatch"
  32. UPSERT = "upsert"
  33. DELETE = "delete"
  34. def _cleanup(path):
  35. sane_path = pathutils.strip_path(path)
  36. attributes = sane_path.split("/") if sane_path else []
  37. if len(attributes) < 2:
  38. return ""
  39. return attributes[0] + "/" + attributes[1]
  40. class HookNotificationItem:
  41. def __init__(self, notification_item_type, path, content=None, uid=None, new_content=None, old_content=None):
  42. self.type = notification_item_type.value
  43. self.point = _cleanup(path)
  44. self._content_legacy = content
  45. self.uid = uid
  46. self.new_content = new_content
  47. self.old_content = old_content
  48. @property
  49. def content(self): # For backward compatibility
  50. return self._content_legacy or self.uid or self.new_content or self.old_content
  51. @property
  52. def replaces_existing_item(self) -> bool:
  53. """Check if this notification item replaces/deletes an existing item."""
  54. return self.old_content is not None
  55. def to_json(self):
  56. return json.dumps(
  57. {**self.__dict__, "content": self.content},
  58. sort_keys=True,
  59. indent=4
  60. )