feat(espocrm): add caching for entity definitions and implement related entity listing

This commit is contained in:
bsiggel
2026-03-08 17:51:36 +00:00
parent a53051ea8e
commit d7b2b5543f
2 changed files with 126 additions and 62 deletions

View File

@@ -2,6 +2,7 @@
import aiohttp
import asyncio
import logging
import time
from typing import Optional, Dict, Any, List
import os
@@ -56,6 +57,8 @@ class EspoCRMAPI:
self.logger.info(f"EspoCRM API initialized with base URL: {self.api_base_url}")
self._session: Optional[aiohttp.ClientSession] = None
self._entity_defs_cache: Dict[str, Dict[str, Any]] = {}
self._entity_defs_cache_ttl_seconds = int(os.getenv('ESPOCRM_METADATA_TTL_SECONDS', '300'))
# Optional Redis for caching/rate limiting (centralized)
self.redis_client = get_redis_client(strict=False)
@@ -81,6 +84,21 @@ class EspoCRMAPI:
if self._session and not self._session.closed:
await self._session.close()
async def get_entity_def(self, entity_type: str) -> Dict[str, Any]:
now = time.monotonic()
cached = self._entity_defs_cache.get(entity_type)
if cached and (now - cached['ts']) < self._entity_defs_cache_ttl_seconds:
return cached['data']
try:
data = await self.api_call(f"/Metadata/EntityDefs/{entity_type}", method='GET')
except EspoCRMAPIError:
all_defs = await self.api_call("/Metadata/EntityDefs", method='GET')
data = all_defs.get(entity_type, {}) if isinstance(all_defs, dict) else {}
self._entity_defs_cache[entity_type] = {'ts': now, 'data': data}
return data
async def api_call(
self,
endpoint: str,
@@ -228,6 +246,36 @@ class EspoCRMAPI:
self._log(f"Listing {entity_type} entities")
return await self.api_call(f"/{entity_type}", method='GET', params=params)
async def list_related(
self,
entity_type: str,
entity_id: str,
link: str,
where: Optional[List[Dict]] = None,
select: Optional[str] = None,
order_by: Optional[str] = None,
order: Optional[str] = None,
offset: int = 0,
max_size: int = 50
) -> Dict[str, Any]:
params = {
'offset': offset,
'maxSize': max_size
}
if where:
import json
params['where'] = where if isinstance(where, str) else json.dumps(where)
if select:
params['select'] = select
if order_by:
params['orderBy'] = order_by
if order:
params['order'] = order
self._log(f"Listing related {entity_type}/{entity_id}/{link}")
return await self.api_call(f"/{entity_type}/{entity_id}/{link}", method='GET', params=params)
async def create_entity(
self,
entity_type: str,