Skip to content

Commit 6c59ca3

Browse files
authored
Merge pull request #114 from singnet/SPS-18
Optimization, rework and refactor
2 parents 28c6abb + ce69a12 commit 6c59ca3

11 files changed

Lines changed: 515 additions & 769 deletions

snet/sdk/__init__.py

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
import google.protobuf.internal.api_implementation
1010
from google.protobuf import symbol_database as _symbol_database
1111

12-
from snet.sdk.registry.models import StorageType
12+
from snet.sdk.exceptions import NoGroupsFoundError, GroupNotFoundError, ServiceMetadataMismatchError
13+
from snet.sdk.registry.models import StorageType, FileURI
1314
from snet.sdk.registry.organization_metadata import OrganizationMetadata
1415
from snet.sdk.registry.registry_contract import RegistryContract
15-
from snet.sdk.registry.service_metadata import MPEServiceMetadata, ServiceMetadata
16+
from snet.sdk.registry.service_metadata import ServiceMetadata, Group
1617

1718
with warnings.catch_warnings():
1819
# Suppress the eth-typing package`s warnings related to some new networks
@@ -74,15 +75,18 @@ def create_service_client(
7475
options=None,
7576
concurrent_calls: int = 1,
7677
):
77-
78-
# Create and instance of the Config object,
79-
# so we can create an instance of ClientLibGenerator
78+
service_metadata = self._enhance_service_metadata(org_id, service_id)
8079
lib_generator = ClientLibGenerator(self.storage_provider, org_id, service_id)
8180

82-
# Download the proto file and generate stubs if needed
81+
if service_metadata.service_api_source is not None:
82+
service_api_source = service_metadata.service_api_source
83+
else:
84+
service_api_source = service_metadata.model_ipfs_hash
85+
service_api_source = FileURI.from_raw_uri(service_api_source)
86+
8387
force_update = config.FORCE_UPDATE
8488
if force_update:
85-
lib_generator.generate_client_library()
89+
lib_generator.generate_client_library(service_api_source)
8690
else:
8791
path_to_pb_files = lib_generator.proto_dir
8892
pb_2_file_name = find_file_by_keyword(
@@ -93,7 +97,7 @@ def create_service_client(
9397
)
9498
if not pb_2_file_name or not pb_2_grpc_file_name:
9599
print("Generating client library...")
96-
lib_generator.generate_client_library()
100+
lib_generator.generate_client_library(service_api_source)
97101

98102
if options is None:
99103
options = dict()
@@ -103,16 +107,14 @@ def create_service_client(
103107
if payment_strategy is None:
104108
payment_strategy = payment_strategy_type.value()
105109

106-
service_metadata = self._enhance_service_metadata(org_id, service_id)
107-
group = self._get_service_group_details(service_metadata, group_name)
110+
group = self._get_service_group(org_id, service_id, service_metadata, group_name)
108111

109112
service_stubs = self.get_service_stub(lib_generator)
110113

111114
pb2_module = self.get_module_by_keyword("pb2.py", lib_generator)
112115
_service_client = ServiceClient(
113116
org_id,
114117
service_id,
115-
service_metadata,
116118
group,
117119
service_stubs,
118120
payment_strategy,
@@ -162,36 +164,28 @@ def get_module_by_keyword(self, keyword: str, lib_generator: ClientLibGenerator)
162164
module_name = os.path.splitext(file_name)[0]
163165
return ModuleName(module_name)
164166

165-
def get_service_metadata(self, org_id, service_id):
167+
def get_service_metadata(self, org_id, service_id) -> ServiceMetadata:
166168
service = self.registry_contract.get_service(org_id, service_id)
167169
return self.storage_provider.fetch_service_metadata(service.metadata_uri)
168170

169171
def get_organization_metadata(self, org_id: str) -> OrganizationMetadata:
170172
org = self.registry_contract.get_org(org_id)
171173
return self.storage_provider.fetch_org_metadata(org.metadata_uri)
172174

173-
def _get_first_group(self, service_metadata: MPEServiceMetadata) -> dict:
174-
return service_metadata["groups"][0]
175-
176-
def _get_group_by_group_name(
177-
self, service_metadata: MPEServiceMetadata, group_name: str
178-
) -> dict:
179-
for group in service_metadata["groups"]:
180-
if group["group_name"] == group_name:
181-
return group
182-
# TODO: configure exceptions
183-
raise Exception()
184-
185-
def _get_service_group_details(
186-
self, service_metadata: MPEServiceMetadata, group_name: str
187-
) -> dict:
188-
if len(service_metadata["groups"]) == 0:
189-
raise Exception("No Groups found for given service, Please add group to the service")
175+
def _get_service_group(
176+
self, org_id: str, service_id: str, service_metadata: ServiceMetadata, group_name: str
177+
) -> Group:
178+
if len(service_metadata.groups) == 0:
179+
raise NoGroupsFoundError(org_id, service_id)
190180

191181
if group_name is None:
192-
return self._get_first_group(service_metadata)
182+
return service_metadata.groups[0]
193183

194-
return self._get_group_by_group_name(service_metadata, group_name)
184+
for group in service_metadata.groups:
185+
if group.group_name == group_name:
186+
return group
187+
188+
raise GroupNotFoundError(org_id, service_id, group_name)
195189

196190
def get_organization_list(self) -> list:
197191
return self.registry_contract.list_orgs()
@@ -215,13 +209,68 @@ def publish_service_comprehensively(
215209
5. publish service into Registry contract
216210
"""
217211
proto_uri = self.storage_provider.publish_proto(proto_dir, storage_type)
218-
219212
metadata.service_api_source = str(proto_uri)
220213
metadata.mpe_address = self.mpe_contract.contract.address
221214

215+
self._check_and_update_service_groups(org_id, metadata.groups)
222216
metadata_uri = self.storage_provider.publish_service_metadata(metadata, storage_type)
217+
223218
receipt = self.registry_contract.create_service(
224219
self.account, org_id, service_id, metadata_uri
225220
)
226221

227222
return receipt["status"] != 0
223+
224+
def update_service(
225+
self,
226+
org_id: str,
227+
service_id: str,
228+
metadata: ServiceMetadata,
229+
proto_dir: Union[str, Path, None] = None,
230+
storage_type: StorageType = StorageType.IPFS,
231+
) -> bool:
232+
if proto_dir is not None:
233+
proto_uri = self.storage_provider.publish_proto(proto_dir, storage_type)
234+
metadata.service_api_source = str(proto_uri)
235+
236+
if not metadata.mpe_address:
237+
metadata.mpe_address = self.mpe_contract.contract.address
238+
239+
self._check_and_update_service_groups(org_id, metadata.groups)
240+
metadata_uri = self.storage_provider.publish_service_metadata(metadata, storage_type)
241+
242+
receipt = self.registry_contract.update_service_metadata(
243+
self.account, org_id, service_id, metadata_uri
244+
)
245+
246+
return receipt["status"] != 0
247+
248+
def update_organization(
249+
self,
250+
org_id: str,
251+
organization_metadata: OrganizationMetadata,
252+
storage_type: StorageType = StorageType.IPFS,
253+
) -> bool:
254+
metadata_uri = self.storage_provider.publish_organization_metadata(
255+
organization_metadata, storage_type
256+
)
257+
receipt = self.registry_contract.update_org_metadata(self.account, org_id, metadata_uri)
258+
259+
return receipt["status"] != 0
260+
261+
def _check_and_update_service_groups(
262+
self, org_id: str, service_groups: list[Group]
263+
) -> list[Group]:
264+
org = self.registry_contract.get_org(org_id)
265+
org_metadata = self.storage_provider.fetch_org_metadata(org.metadata_uri)
266+
org_groups_map = {g.group_name: g for g in org_metadata.groups}
267+
268+
for group in service_groups:
269+
try:
270+
group.group_id = org_groups_map[group.group_name].group_id
271+
except KeyError:
272+
raise ServiceMetadataMismatchError(
273+
"All groups added to the service must also exist in the organization!"
274+
)
275+
276+
return service_groups

snet/sdk/client_lib_generator.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
from pathlib import Path
33

4+
from snet.sdk.registry.models import FileURI
45
from snet.sdk.registry.storage_provider import StorageProvider
56
from snet.sdk.utils.utils import compile_proto
67

@@ -20,9 +21,9 @@ def __init__(
2021
self.proto_dir: Path = proto_dir if proto_dir else Path.home().joinpath(".snet")
2122
self.generate_directories_by_params()
2223

23-
def generate_client_library(self) -> None:
24+
def generate_client_library(self, service_api_source: FileURI) -> None:
2425
try:
25-
self.receive_proto_files()
26+
self.receive_proto_files(service_api_source)
2627
compilation_result = compile_proto(
2728
entry_path=self.proto_dir,
2829
codegen_dir=self.proto_dir,
@@ -35,8 +36,8 @@ def generate_client_library(self) -> None:
3536
f'in org with id "{self.org_id}" '
3637
f"generated at {self.proto_dir}"
3738
)
38-
except Exception as e:
39-
print(str(e))
39+
except Exception:
40+
raise Exception("Error while proto compilation!")
4041

4142
def generate_directories_by_params(self) -> None:
4243
if not self.proto_dir.is_absolute():
@@ -47,13 +48,7 @@ def create_service_client_libraries_path(self) -> None:
4748
self.proto_dir = self.proto_dir.joinpath(self.org_id, self.service_id, self.language)
4849
self.proto_dir.mkdir(parents=True, exist_ok=True)
4950

50-
def receive_proto_files(self) -> None:
51-
metadata = self._metadata_provider.fetch_service_metadata(
52-
org_id=self.org_id, service_id=self.service_id
53-
)
54-
service_api_source = metadata.get("service_api_source") or metadata.get("model_ipfs_hash")
55-
56-
# Receive proto files
51+
def receive_proto_files(self, service_api_source: FileURI) -> None:
5752
if self.proto_dir.exists():
5853
self._metadata_provider.fetch_and_extract_proto(service_api_source, self.proto_dir)
5954
else:

snet/sdk/exceptions.py

Lines changed: 108 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
from grpc import RpcError
2+
3+
14
class SnetSDKError(Exception):
5+
"""Base SNET SDK exception class"""
6+
27
pass
38

49

10+
# ==================== Blockchain interaction Errors ====================
11+
12+
513
class ContractError(SnetSDKError):
614
pass
715

@@ -27,7 +35,7 @@ def __init__(self, tx_hash: str, event_name: str):
2735
)
2836

2937

30-
class RegistryContractError(ContractError):
38+
class RegistryContractError(ValueError, ContractError):
3139
pass
3240

3341

@@ -41,18 +49,28 @@ def __init__(self, org_id: str, service_id: str):
4149
super().__init__(f"Service with org_id={org_id} service_id={service_id} doesn't exist!")
4250

4351

44-
class UnauthorizedCallerError(TransactionError):
52+
class UnauthorizedCallerError(RegistryContractError):
4553
pass
4654

4755

4856
class UnauthorizedOrgMemberError(UnauthorizedCallerError):
4957
def __init__(self, org_id: str, address: str):
50-
super().__init__(f"Address {address} isn't owner or member of the organization {org_id}!")
58+
super().__init__(
59+
f"Address {address} isn't an owner or a member of the organization {org_id}!"
60+
)
5161

5262

53-
class UnsupportedStorageTypeError(ValueError, SnetSDKError):
54-
def __init__(self, storage_type: str):
55-
super().__init__(f"Unsupported storage type: {storage_type}!")
63+
class UnauthorizedOrgOwnerError(UnauthorizedCallerError):
64+
def __init__(self, org_id: str, address: str):
65+
super().__init__(f"Address {address} isn't an owner of the organization {org_id}!")
66+
67+
68+
class IncorrectWalletAddressError(RegistryContractError):
69+
def __init__(self, address: str):
70+
super().__init__(f"Address {address} is not a correct Ethereum address!")
71+
72+
73+
# ==================== Metadata Errors ====================
5674

5775

5876
class MetadataMismatchError(ValueError, SnetSDKError):
@@ -67,18 +85,40 @@ class OrganizationMetadataMismatchError(MetadataMismatchError):
6785
pass
6886

6987

88+
# ==================== Storage Provider Errors ====================
89+
90+
7091
class StorageProviderError(SnetSDKError):
7192
pass
7293

7394

95+
class UnsupportedStorageTypeError(ValueError, StorageProviderError):
96+
def __init__(self, storage_type: str):
97+
super().__init__(f"Unsupported storage type: {storage_type}!")
98+
99+
74100
class LighthouseError(StorageProviderError):
75101
def __init__(self):
76102
super().__init__(
77103
"Lighthouse internal error! Most likely, you did not specify LIGHTHOUSE_TOKEN in the config or it expired."
78104
)
79105

80106

81-
class PublishProtoError(StorageProviderError, ValueError):
107+
class IPFSError(StorageProviderError):
108+
pass
109+
110+
111+
class IPFSHashMismatchError(IPFSError):
112+
def __init__(self):
113+
super().__init__("IPFS hash mismatch with data")
114+
115+
116+
class IPFSHashCheckError(IPFSError):
117+
def __init__(self):
118+
super().__init__("IPFS hash integrity check failed!")
119+
120+
121+
class PublishProtoError(ValueError, StorageProviderError):
82122
pass
83123

84124

@@ -90,3 +130,64 @@ def __init__(self, dir_path: str):
90130
class ProtoFilesNotFoundError(PublishProtoError):
91131
def __init__(self, dir_path: str):
92132
super().__init__(f"Cannot find any .proto file in {dir_path}!")
133+
134+
135+
class ExtractingProtoError(ValueError, StorageProviderError):
136+
pass
137+
138+
139+
# ==================== Training Errors ====================
140+
141+
142+
class TrainingError(SnetSDKError):
143+
pass
144+
145+
146+
class WrongDatasetError(TrainingError):
147+
def __init__(self, errors: list[str]):
148+
self.errors = errors
149+
exception_msg = "Dataset check failed:\n"
150+
for check in errors:
151+
exception_msg += f"\t{check}\n"
152+
super().__init__(exception_msg)
153+
154+
155+
class WrongMethodError(ValueError, TrainingError):
156+
def __init__(self, method_name: str):
157+
super().__init__(f"Method with name {method_name} not found!")
158+
159+
160+
class NoTrainingError(ValueError, TrainingError):
161+
def __init__(self, org_id: str, service_id: str):
162+
super().__init__(
163+
f"Training is not implemented for the service with org_id={org_id} and service_id={service_id}!"
164+
)
165+
166+
167+
class GRPCError(RpcError, TrainingError):
168+
def __init__(self, error: RpcError):
169+
super().__init__(f"An error occurred during the grpc call: {error}.")
170+
171+
172+
class NoSuchModelError(ValueError, TrainingError):
173+
def __init__(self, model_id: str):
174+
super().__init__(f"Model with id {model_id} not found!")
175+
176+
177+
# ==================== Service Client Errors ====================
178+
179+
180+
class ServiceClientError(SnetSDKError):
181+
pass
182+
183+
184+
class NoGroupsFoundError(ServiceClientError):
185+
def __init__(self, org_id: str, service_id: str):
186+
super().__init__(f"Service with org_id={org_id} service_id={service_id} has no groups!")
187+
188+
189+
class GroupNotFoundError(ServiceClientError):
190+
def __init__(self, org_id: str, service_id: str, group_name: str):
191+
super().__init__(
192+
f"Service with org_id={org_id} service_id={service_id} has no group with group_name={group_name}!"
193+
)

0 commit comments

Comments
 (0)