from __future__ import annotations import hashlib from datetime import datetime, timezone from .models import ACL, Change, IngestionBatch, NormalizedDocument, Provenance, SyncCursor class SharePointConnector: name = "sharepoint" def __init__(self, graph, secret_provider, config): self.graph, self.secrets, self.config = graph, secret_provider, config self.scope = ":".join(config[x] for x in ('site_id','drive_id')) + ':' + config.get('folder_path','') def _headers(self): return {"Authorization": f"Bearer {self.secrets.get(self.config['credential_ref'])}"} def _doc(self, tenant_id, item, event_id=None): body = item.get('text', '') now = datetime.now(timezone.utc) return NormalizedDocument(document_id=f"sharepoint:{item['id']}", tenant_id=tenant_id, title=item['name'], body=body, mime_type=item['mime_type'], source_path=item['web_url'], content_hash=hashlib.sha256(body.encode()).hexdigest(), updated_at=item['last_modified'], acl=ACL(**item.get('acl', {})), provenance=Provenance(connector=self.name, source_uri=item['web_url'], source_id=item['id'], revision_id=item.get('e_tag'), revision_time=item['last_modified'], retrieved_at=now, webhook_event_id=event_id)) def full(self, tenant_id): items = self.graph.list_files(self.config, self._headers()) allowed = set(self.config.get('mime_types', ['application/pdf','application/vnd.openxmlformats-officedocument.wordprocessingml.document','text/html'])) docs = [self._doc(tenant_id, x) for x in items if x['mime_type'] in allowed] return IngestionBatch(run_id=hashlib.sha256(self.scope.encode()).hexdigest()[:16], tenant_id=tenant_id, documents=docs, next_cursor=SyncCursor(connector=self.name, scope_fingerprint=self.scope, value=items[-1].get('delta_token') if items else None, version=1, updated_at=datetime.now(timezone.utc))) def incremental(self, tenant_id, cursor): items, token = self.graph.delta(self.config, cursor.value if cursor else None, self._headers()) docs = [self._doc(tenant_id, x) for x in items if not x.get('deleted')] return IngestionBatch(run_id=hashlib.sha256(self.scope.encode()).hexdigest()[:16], tenant_id=tenant_id, documents=docs, next_cursor=SyncCursor(connector=self.name, scope_fingerprint=self.scope, value=token, version=(cursor.version+1 if cursor else 1), updated_at=datetime.now(timezone.utc))) def changes_from_webhook(self, payload): return [Change(path=r['resource'], source_id=r['resource'], revision_id=r.get('resourceData', {}).get('id')) for r in payload.get('value', [])]