Skip to content

Commit a2ea25d

Browse files
Fix merging storage which contains specific nested structures (#159)
1 parent 41fb7fe commit a2ea25d

File tree

4 files changed

+100
-6
lines changed

4 files changed

+100
-6
lines changed

scripts/init_demos.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#$/bin/bash
22
for name in `ls src | grep demo`
33
do
4-
dipdup -c src/$name/dipdup.yml init
4+
dipdup -c src/$name/dipdup.yml init --overwrite-types
55
dipdup -c src/$name/dipdup.yml -c src/$name/dipdup-docker.yml docker init
66
done

src/dipdup/models.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from pydantic.dataclasses import dataclass
1010
from pydantic.error_wrappers import ValidationError
1111
from tortoise import Model, fields
12+
from typing_extensions import get_args
1213

1314
from dipdup.enums import IndexStatus, IndexType
1415
from dipdup.exceptions import ConfigurationError, InvalidDataError, ReindexingReason
@@ -77,7 +78,6 @@ def _process_storage(
7778
prefix: str = None,
7879
) -> Dict[str, Any]:
7980
for key, field in storage_type.__fields__.items():
80-
8181
if key == '__root__':
8282
continue
8383

@@ -95,8 +95,16 @@ def _process_storage(
9595
continue
9696
raise ConfigurationError(f'Type `{storage_type.__name__}` is invalid: `{key}` field does not exists') from e
9797

98-
# FIXME: Pydantic bug. `BaseModel.type_` returns incorrect value when annotation is Dict[str, bool]
99-
if field.type_ != field.outer_type_ and field.type_ == bool:
98+
# FIXME: Pydantic bug? I have no idea how does it work, this workaround is just a guess.
99+
# FIXME: `BaseModel.type_` returns incorrect value when annotation is Dict[str, bool], Dict[str, BaseModel], and possibly any other cases.
100+
is_complex_type = field.type_ != field.outer_type_
101+
try:
102+
get_args(field.outer_type_)[1].__fields__
103+
is_nested_dict_model = True
104+
except Exception:
105+
is_nested_dict_model = False
106+
107+
if is_complex_type and (field.type_ == bool or is_nested_dict_model):
100108
annotation = field.outer_type_
101109
else:
102110
annotation = field.type_

tests/test_dipdup/models.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# generated by datamodel-codegen:
2+
# filename: storage.json
3+
4+
from __future__ import annotations
5+
6+
from typing import Dict, List, Optional
7+
8+
from pydantic import BaseModel, Extra
9+
10+
11+
class ResourceMap(BaseModel):
12+
class Config:
13+
extra = Extra.forbid
14+
15+
id: str
16+
rate: str
17+
18+
19+
class ResourceCollectorStorage(BaseModel):
20+
class Config:
21+
extra = Extra.forbid
22+
23+
administrator: str
24+
current_user: Optional[str]
25+
default_start_time: str
26+
generation_rate: str
27+
managers: List[str]
28+
metadata: Dict[str, str]
29+
nft_registry: str
30+
paused: bool
31+
resource_map: Dict[str, ResourceMap]
32+
resource_registry: str
33+
tezotop_collection: Dict[str, str]
34+
35+
36+
# 'resource_map': {
37+
# 'type': 'object',
38+
# 'propertyNames': {'type': 'string', '$comment': 'string'},
39+
# 'additionalProperties': {
40+
# 'type': 'object',
41+
# 'properties': {'id': {'type': 'string', '$comment': 'nat'}, 'rate': {'type': 'string', '$comment': 'nat'}},
42+
# 'required': ['id', 'rate'],
43+
# 'additionalProperties': False,
44+
# '$comment': 'pair',
45+
# },
46+
# '$comment': 'map',
47+
# },

tests/test_dipdup/test_models.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
from datetime import datetime
12
from unittest import TestCase
23

34
from demo_tezos_domains.types.name_registry.storage import NameRegistryStorage
45
from dipdup.models import OperationData
6+
from tests.test_dipdup.models import ResourceCollectorStorage
57

68

79
class ModelsTest(TestCase):
8-
def test_merged_storage(self):
10+
def test_merged_storage(self) -> None:
911
storage = {
1012
'store': {
1113
'data': 15023,
@@ -76,7 +78,7 @@ def test_merged_storage(self):
7678
type='transaction',
7779
id=0,
7880
level=0,
79-
timestamp=0,
81+
timestamp=datetime.now(),
8082
hash='',
8183
counter=0,
8284
sender_address='',
@@ -88,3 +90,40 @@ def test_merged_storage(self):
8890
)
8991
merged_storage = operation_data.get_merged_storage(NameRegistryStorage)
9092
self.assertTrue('6672657175656e742d616e616c7973742e65646f' in merged_storage.store.records)
93+
94+
def test_merged_storage_dict_of_dicts(self) -> None:
95+
storage = {
96+
'paused': False,
97+
'managers': ['tz1VPZyh4ZHjDDpgvznqQQXUCLcV7g91WGMz'],
98+
'metadata': 43542,
99+
'current_user': 'tz1VPZyh4ZHjDDpgvznqQQXUCLcV7g91WGMz',
100+
'nft_registry': 'KT1SZ87ihAWc43YZxYjoRz8MQyAapUGbZigG',
101+
'resource_map': {
102+
'enr': {'id': '2', 'rate': '1875000'},
103+
'mch': {'id': '3', 'rate': '625000'},
104+
'min': {'id': '1', 'rate': '1250000'},
105+
'uno': {'id': '0', 'rate': '1250'},
106+
},
107+
'administrator': 'tz1VPZyh4ZHjDDpgvznqQQXUCLcV7g91WGMz',
108+
'generation_rate': '9',
109+
'resource_registry': 'KT1SLaZNaDF7V6Lt8FXTbYNqkBS81gjHXMsP',
110+
'default_start_time': '1630678200',
111+
'tezotop_collection': 43543,
112+
}
113+
operation_data = OperationData(
114+
storage=storage,
115+
diffs=None,
116+
type='transaction',
117+
id=0,
118+
level=0,
119+
timestamp=datetime.now(),
120+
hash='',
121+
counter=0,
122+
sender_address='',
123+
target_address='',
124+
initiator_address='',
125+
amount=0,
126+
status='',
127+
has_internals=False,
128+
)
129+
operation_data.get_merged_storage(ResourceCollectorStorage)

0 commit comments

Comments
 (0)