Skip to content

Commit 4db1b57

Browse files
committed
Added: custom date serializer (#73)
1 parent c21ab86 commit 4db1b57

File tree

5 files changed

+176
-285
lines changed

5 files changed

+176
-285
lines changed

libreforms_fastapi/app/__init__.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ async def api_form_create(
413413
# # Here we validate and coerce data into its proper type
414414
form_data = FormModel.model_validate(body)
415415
json_data = form_data.model_dump_json()
416+
data_dict = form_data.model_dump()
416417

417418
# Ugh, I'd like to find a more efficient way to get the user data. But alas, that
418419
# the sqlalchemy-signing table is not optimized alongside the user model...
@@ -443,7 +444,8 @@ async def api_form_create(
443444
# doc_db.create_document,
444445
d = doc_db.create_document(
445446
form_name=form_name,
446-
json_data=json_data,
447+
# json_data=json_data,
448+
data_dict=data_dict,
447449
metadata=metadata,
448450
)
449451

@@ -626,6 +628,7 @@ async def api_form_update(
626628
# # Here we validate and coerce data into its proper type
627629
form_data = FormModel.model_validate(body)
628630
json_data = form_data.model_dump_json()
631+
data_dict = form_data.model_dump()
629632

630633
# print("\n\n\n", json_data)
631634

@@ -660,7 +663,8 @@ async def api_form_update(
660663
d = doc_db.update_document(
661664
form_name=form_name,
662665
document_id=document_id,
663-
json_data=json_data,
666+
# json_data=json_data,
667+
updated_data_dict=data_dict,
664668
metadata=metadata,
665669
limit_users=limit_query_to,
666670
)

libreforms_fastapi/utils/certificates.py

-23
Original file line numberDiff line numberDiff line change
@@ -113,29 +113,6 @@ def sign_data(self, data):
113113
)
114114
return signature
115115

116-
# def verify_signature(self, data, signature):
117-
# """
118-
# Verifies the signature of the data using the public key.
119-
# """
120-
# with open(self.get_public_key_file(), "rb") as key_file:
121-
# public_key = serialization.load_pem_public_key(
122-
# key_file.read(),
123-
# backend=default_backend()
124-
# )
125-
126-
# try:
127-
# public_key.verify(
128-
# signature,
129-
# data,
130-
# padding.PSS(
131-
# mgf=padding.MGF1(hashes.SHA256()),
132-
# salt_length=padding.PSS.MAX_LENGTH
133-
# ),
134-
# hashes.SHA256()
135-
# )
136-
# return True
137-
# except Exception as e:
138-
# return False
139116
def verify_signature(self, data, signature, public_key=None):
140117
"""
141118
Verifies the signature of the data using the provided public key.
+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import json
2+
from datetime import date
3+
from json import JSONEncoder
4+
from bson import ObjectId
5+
6+
from tinydb import (
7+
TinyDB,
8+
Query,
9+
Storage
10+
)
11+
from tinydb.table import (
12+
Table as TinyTable,
13+
Document
14+
)
15+
16+
from typing import (
17+
Mapping,
18+
Union,
19+
Iterable,
20+
List,
21+
)
22+
23+
24+
class DateEncoder(JSONEncoder):
25+
"""We need to convert date objects to 'YYYY-MM-DD' format"""
26+
def default(self, obj):
27+
if isinstance(obj, date):
28+
return obj.isoformat()
29+
# Fall back to the superclass method for other types
30+
return JSONEncoder.default(self, obj)
31+
32+
# We want to modify TinyDB use use string representations of bson
33+
# ObjectIDs. As such, we will need to modify some underlying behavior,
34+
# see https://github.com/signebedi/libreforms-fastapi/issues/15.
35+
class CustomTable(TinyTable):
36+
document_id_class = str # Use string IDs instead of integers
37+
38+
def _get_next_id(self, document_id=str(ObjectId())):
39+
"""
40+
Generate a new BSON ObjectID string to use as the TinyDB document ID.
41+
"""
42+
return document_id
43+
44+
45+
def insert(self, document: Mapping, document_id:Union[str, bool]=False) -> int:
46+
"""
47+
Insert a new document into the table.
48+
49+
:param document: the document to insert
50+
:returns: the inserted document's ID
51+
"""
52+
53+
if not document_id:
54+
document_id = str(ObjectId())
55+
56+
# Make sure the document implements the ``Mapping`` interface
57+
if not isinstance(document, Mapping):
58+
raise ValueError('Document is not a Mapping')
59+
60+
# First, we get the document ID for the new document
61+
if isinstance(document, Document):
62+
# For a `Document` object we use the specified ID
63+
doc_id = document.doc_id
64+
65+
# We also reset the stored next ID so the next insert won't
66+
# re-use document IDs by accident when storing an old value
67+
self._next_id = None
68+
else:
69+
# In all other cases we use the next free ID
70+
doc_id = self._get_next_id(document_id=document_id)
71+
72+
# Now, we update the table and add the document
73+
def updater(table: dict):
74+
if doc_id in table:
75+
raise ValueError(f'Document with ID {str(doc_id)} '
76+
f'already exists')
77+
78+
# By calling ``dict(document)`` we convert the data we got to a
79+
# ``dict`` instance even if it was a different class that
80+
# implemented the ``Mapping`` interface
81+
table[doc_id] = dict(document)
82+
83+
# See below for details on ``Table._update``
84+
self._update_table(updater)
85+
86+
return doc_id
87+
88+
def insert_multiple(self, documents: Iterable[Mapping], document_ids:Union[List, bool]=False) -> List[int]:
89+
"""
90+
Insert multiple documents into the table.
91+
92+
:param documents: an Iterable of documents to insert
93+
:returns: a list containing the inserted documents' IDs
94+
"""
95+
doc_ids = []
96+
97+
if document_ids and len(document_ids) != len(documents):
98+
raise Exception("When inserting multiple and passing your own document_ids," \
99+
"the list must be the same length as the document list")
100+
101+
def updater(table: dict):
102+
# for document in documents:
103+
for i, document in enumerate(documents):
104+
105+
# Make sure the document implements the ``Mapping`` interface
106+
if not isinstance(document, Mapping):
107+
raise ValueError('Document is not a Mapping')
108+
109+
if isinstance(document, Document):
110+
# Check if document does not override an existing document
111+
if document.doc_id in table:
112+
raise ValueError(
113+
f'Document with ID {str(document.doc_id)} '
114+
f'already exists'
115+
)
116+
117+
# Store the doc_id, so we can return all document IDs
118+
# later. Then save the document with its doc_id and
119+
# skip the rest of the current loop
120+
doc_id = document.doc_id
121+
doc_ids.append(doc_id)
122+
table[doc_id] = dict(document)
123+
continue
124+
125+
# Generate new document ID for this document
126+
# Store the doc_id, so we can return all document IDs
127+
# later, then save the document with the new doc_id
128+
if not document_ids:
129+
document_id = str(ObjectId())
130+
else:
131+
document_id = document_ids[i]
132+
doc_id = self._get_next_id()
133+
doc_ids.append(doc_id)
134+
table[doc_id] = dict(document)
135+
136+
# See below for details on ``Table._update``
137+
self._update_table(updater)
138+
139+
return doc_ids
140+
141+
# Subclass TinyDB and override the table_class attribute with our new logic
142+
class CustomTinyDB(TinyDB):
143+
table_class = CustomTable
144+

0 commit comments

Comments
 (0)