Mercurial > libervia-backend
comparison sat/memory/sqla_mapping.py @ 3593:cb8d0e8b917f
core (memory/sqla_mapping): mapping for PubsubNode and PubsubItem (will be used for caching)
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 29 Jul 2021 21:38:37 +0200 |
parents | 16ade4ad63f3 |
children | 05db744f194f |
comparison
equal
deleted
inserted
replaced
3592:ef144aaea2bb | 3593:cb8d0e8b917f |
---|---|
16 # You should have received a copy of the GNU Affero General Public License | 16 # You should have received a copy of the GNU Affero General Public License |
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | 18 |
19 import pickle | 19 import pickle |
20 import json | 20 import json |
21 from datetime import datetime | |
22 import time | |
23 import enum | |
21 from sqlalchemy import ( | 24 from sqlalchemy import ( |
22 MetaData, Column, Integer, Text, Float, Enum, ForeignKey, UniqueConstraint, Index, | 25 MetaData, Column, Integer, Text, Float, Boolean, DateTime, Enum, JSON, ForeignKey, |
26 UniqueConstraint, Index | |
23 ) | 27 ) |
24 | 28 |
25 from sqlalchemy.orm import declarative_base, relationship | 29 from sqlalchemy.orm import declarative_base, relationship |
26 from sqlalchemy.types import TypeDecorator | 30 from sqlalchemy.types import TypeDecorator |
31 from sqlalchemy.sql.functions import now | |
27 from twisted.words.protocols.jabber import jid | 32 from twisted.words.protocols.jabber import jid |
28 from datetime import datetime | 33 from wokkel import generic |
29 | 34 |
30 | 35 |
31 Base = declarative_base( | 36 Base = declarative_base( |
32 metadata=MetaData( | 37 metadata=MetaData( |
33 naming_convention={ | 38 naming_convention={ |
42 # keys which are in message data extra but not stored in extra field this is | 47 # keys which are in message data extra but not stored in extra field this is |
43 # because those values are stored in separate fields | 48 # because those values are stored in separate fields |
44 NOT_IN_EXTRA = ('stanza_id', 'received_timestamp', 'update_uid') | 49 NOT_IN_EXTRA = ('stanza_id', 'received_timestamp', 'update_uid') |
45 | 50 |
46 | 51 |
52 class SyncState(enum.Enum): | |
53 #: synchronisation is currently in progress | |
54 IN_PROGRESS = 1 | |
55 #: synchronisation is done | |
56 COMPLETED = 2 | |
57 #: something wrong happened during synchronisation, won't sync | |
58 ERROR = 3 | |
59 #: synchronisation won't be done even if a syncing analyser match | |
60 NO_SYNC = 4 | |
61 | |
62 | |
47 class LegacyPickle(TypeDecorator): | 63 class LegacyPickle(TypeDecorator): |
48 """Handle troubles with data pickled by former version of SàT | 64 """Handle troubles with data pickled by former version of SàT |
49 | 65 |
50 This type is temporary until we do migration to a proper data type | 66 This type is temporary until we do migration to a proper data type |
51 """ | 67 """ |
93 | 109 |
94 def process_result_value(self, value, dialect): | 110 def process_result_value(self, value, dialect): |
95 if value is None: | 111 if value is None: |
96 return {} | 112 return {} |
97 return json.loads(value) | 113 return json.loads(value) |
114 | |
115 | |
116 class Xml(TypeDecorator): | |
117 impl = Text | |
118 cache_ok = True | |
119 | |
120 def process_bind_param(self, value, dialect): | |
121 if value is None: | |
122 return None | |
123 return value.toXml() | |
124 | |
125 def process_result_value(self, value, dialect): | |
126 if value is None: | |
127 return None | |
128 return generic.parseXml(value.encode()) | |
98 | 129 |
99 | 130 |
100 class JID(TypeDecorator): | 131 class JID(TypeDecorator): |
101 """Store twisted JID in text fields""" | 132 """Store twisted JID in text fields""" |
102 impl = Text | 133 impl = Text |
446 access = Column(JsonDefaultDict) | 477 access = Column(JsonDefaultDict) |
447 extra = Column(JsonDefaultDict) | 478 extra = Column(JsonDefaultDict) |
448 profile_id = Column(ForeignKey("profiles.id", ondelete="CASCADE")) | 479 profile_id = Column(ForeignKey("profiles.id", ondelete="CASCADE")) |
449 | 480 |
450 profile = relationship("Profile") | 481 profile = relationship("Profile") |
482 | |
483 | |
484 class PubsubNode(Base): | |
485 __tablename__ = "pubsub_nodes" | |
486 __table_args__ = ( | |
487 UniqueConstraint("profile_id", "service", "name"), | |
488 ) | |
489 | |
490 id = Column(Integer, primary_key=True) | |
491 profile_id = Column( | |
492 ForeignKey("profiles.id", ondelete="CASCADE") | |
493 ) | |
494 service = Column(JID) | |
495 name = Column(Text, nullable=False) | |
496 subscribed = Column( | |
497 Boolean(create_constraint=True, name="subscribed_bool"), nullable=False | |
498 ) | |
499 analyser = Column(Text) | |
500 sync_state = Column( | |
501 Enum( | |
502 SyncState, | |
503 name="sync_state", | |
504 create_constraint=True, | |
505 ), | |
506 nullable=True | |
507 ) | |
508 sync_state_updated = Column( | |
509 Float, | |
510 nullable=False, | |
511 default=time.time() | |
512 ) | |
513 type_ = Column( | |
514 Text, name="type", nullable=True | |
515 ) | |
516 subtype = Column( | |
517 Text, nullable=True | |
518 ) | |
519 extra = Column(JSON) | |
520 | |
521 items = relationship("PubsubItem", back_populates="node", passive_deletes=True) | |
522 | |
523 def __str__(self): | |
524 return f"Pubsub node {self.name!r} at {self.service}" | |
525 | |
526 | |
527 class PubsubItem(Base): | |
528 __tablename__ = "pubsub_items" | |
529 __table_args__ = ( | |
530 UniqueConstraint("node_id", "name"), | |
531 ) | |
532 id = Column(Integer, primary_key=True) | |
533 node_id = Column(ForeignKey("pubsub_nodes.id", ondelete="CASCADE"), nullable=False) | |
534 name = Column(Text, nullable=False) | |
535 data = Column(Xml, nullable=False) | |
536 created = Column(DateTime, nullable=False, server_default=now()) | |
537 updated = Column(DateTime, nullable=False, server_default=now(), onupdate=now()) | |
538 parsed = Column(JSON) | |
539 | |
540 node = relationship("PubsubNode", back_populates="items") |