Skip to content

Commit 5df5bf3

Browse files
committed
feat: end implementing service metadata, rework storage provider
1 parent f2af65e commit 5df5bf3

10 files changed

Lines changed: 241 additions & 196 deletions

File tree

snet/sdk/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
)
3434
from snet.sdk.service_client import ServiceClient
3535
from snet.sdk.registry.storage_provider import StorageProvider
36-
from snet.sdk.custom_typing import ModuleName, ServiceStub
36+
from snet.sdk.types import ModuleName, ServiceStub
3737
from snet.sdk.utils.utils import (
3838
find_file_by_keyword,
3939
get_we3_object,

snet/sdk/custom_typing.py

Lines changed: 0 additions & 5 deletions
This file was deleted.

snet/sdk/registry/models.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from dataclasses import dataclass
2+
from enum import Enum
3+
from typing import Union
4+
5+
from snet.sdk.utils.utils import bytes32_to_str
6+
7+
8+
@dataclass
9+
class RawOrgData:
10+
org_id: bytes
11+
metadata_uri: bytes
12+
owner: str
13+
members: list[str]
14+
services: list[bytes] # IDs
15+
16+
17+
@dataclass
18+
class RawServiceData:
19+
service_id: bytes
20+
metadata_uri: bytes
21+
22+
23+
class StorageType(Enum):
24+
IPFS = "ipfs"
25+
FILECOIN = "filecoin"
26+
27+
28+
@dataclass
29+
class FileURI:
30+
storage_type: StorageType
31+
uri_hash: str
32+
33+
@classmethod
34+
def from_raw_uri(cls, string_uri: Union[str, bytes]) -> "FileURI":
35+
if not string_uri:
36+
raise ValueError("'string_uri' cannot be empty!")
37+
38+
if isinstance(string_uri, bytes):
39+
string_uri = string_uri.rstrip(b"\0").decode("ascii")
40+
41+
try:
42+
s_t_str, u_h = string_uri.split("://")
43+
except ValueError:
44+
s_t_str = "ipfs"
45+
u_h = string_uri
46+
47+
s_t = StorageType(s_t_str)
48+
49+
return cls(s_t, u_h)
50+
51+
def __str__(self) -> str:
52+
return f"{self.storage_type.value}://{self.uri_hash}"
53+
54+
@classmethod
55+
def normalize_string_uri(cls, string_uri: str) -> str:
56+
return str(FileURI.from_raw_uri(string_uri))
57+
58+
59+
@dataclass
60+
class OrgData:
61+
org_id: str
62+
metadata_uri: FileURI
63+
owner: str
64+
members: list[str]
65+
services: list[str] # IDs
66+
67+
@classmethod
68+
def from_raw_data(cls, raw_org_data: RawOrgData) -> "OrgData":
69+
return cls(
70+
org_id=bytes32_to_str(raw_org_data.org_id),
71+
metadata_uri=FileURI.from_raw_uri(raw_org_data.metadata_uri),
72+
owner=raw_org_data.owner,
73+
members=raw_org_data.members,
74+
services=list(map(bytes32_to_str, raw_org_data.services)),
75+
)
76+
77+
78+
@dataclass
79+
class ServiceData:
80+
org_id: str
81+
service_id: str
82+
metadata_uri: FileURI
83+
84+
@classmethod
85+
def from_raw_data(
86+
cls, raw_service_data: RawServiceData, org_id: Union[str, bytes]
87+
) -> "ServiceData":
88+
return cls(
89+
org_id=bytes32_to_str(org_id) if isinstance(org_id, bytes) else org_id,
90+
service_id=bytes32_to_str(raw_service_data.service_id),
91+
metadata_uri=FileURI.from_raw_uri(raw_service_data.metadata_uri),
92+
)

snet/sdk/registry/registry_contract.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@
44

55
from snet.sdk.account import Account
66
from snet.sdk.config import config
7-
from snet.sdk.types import RawOrgData, OrgData, ServiceData, RawServiceData
7+
from snet.sdk.registry.models import RawOrgData, OrgData, ServiceData, RawServiceData
88
from snet.sdk.utils.utils import (
99
type_converter,
1010
bytes32_to_str,
1111
get_we3_object,
12-
convert_raw_service_data,
13-
convert_raw_org_data,
1412
)
1513

1614

@@ -29,7 +27,7 @@ def get_org(self, org_id: str) -> OrgData:
2927
# TODO: configure exceptions
3028
raise Exception()
3129

32-
return convert_raw_org_data(
30+
return OrgData.from_raw_data(
3331
RawOrgData(
3432
org_id=found_org_id,
3533
metadata_uri=org_metadata_uri,
@@ -49,7 +47,7 @@ def get_service(self, org_id: str, service_id: str) -> ServiceData:
4947
# TODO: configure exceptions
5048
raise Exception()
5149

52-
return convert_raw_service_data(
50+
return ServiceData.from_raw_data(
5351
RawServiceData(service_id=found_service_id, metadata_uri=service_metadata_uri),
5452
org_id=org_id,
5553
)

snet/sdk/registry/service_metadata.py

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
from pydantic import BaseModel, Field, model_validator, ValidationInfo
4949

50+
from snet.sdk.registry.models import FileURI
5051
from snet.sdk.utils.utils import is_valid_endpoint
5152

5253

@@ -67,46 +68,76 @@ def is_single_value(asset_type):
6768
return True
6869

6970

71+
class FileType(Enum):
72+
IMAGE = "image"
73+
VIDEO = "video"
74+
ARCHIVE = "archive"
75+
76+
77+
# class AssetType(Enum):
78+
# HERO_IMAGE = "hero_image"
79+
# PROTO_FILE = "proto_file"
80+
# DEMO_COMPONENT = "demo_component"
81+
82+
83+
class ServiceType(Enum):
84+
GRPC = "grpc"
85+
HTTP = "http"
86+
JSONRPC = "jsonrpc"
87+
88+
7089
def generate_group_id() -> str:
7190
return base64.b64encode(secrets.token_bytes(32)).decode()
7291

7392

7493
class Pricing(BaseModel):
7594
price_model: Literal["fixed_price", "method_price"] = Field(default="fixed_price")
76-
price_in_cogs: int = Field(ge=1)
95+
price_in_cogs: int = Field(ge=1, default=1)
7796
default: bool = Field(default=True)
7897

7998

8099
class Group(BaseModel):
81100
group_name: str = Field(min_length=1, default="default_group")
82101
group_id: str = Field(default_factory=generate_group_id, init=False)
83-
free_calls: int = Field(ge=1)
84-
free_call_signer_address: str
85-
daemon_addresses: list[str]
102+
free_calls: int = Field(ge=1, default=3)
103+
free_call_signer_address: str = Field(default="")
104+
daemon_addresses: list[str] = Field(default=[])
105+
endpoints: list[str] = Field(default=[])
106+
pricing: list[Pricing] = Field(default=[])
86107

87108

88-
class ServiceDescription(BaseModel): ...
109+
class ServiceDescription(BaseModel):
110+
url: str = Field(default="")
111+
short_description: str = Field(default="")
112+
description: str = Field(default="")
89113

90114

91-
class Media(BaseModel): ...
115+
class Media(BaseModel):
116+
order: int = Field(ge=1, default=1)
117+
url: str = Field(min_length=1)
118+
file_type: FileType
119+
alt_text: str = Field(default="")
120+
asset_type: AssetType
92121

93122

94-
class Contributor(BaseModel): ...
123+
class Contributor(BaseModel):
124+
name: str = Field(min_length=1)
125+
email_id: str = Field(default="")
95126

96127

97128
class ServiceMetadata(BaseModel):
98129
version: int = Field(ge=1, default=1)
99-
display_name: str
130+
display_name: str = Field(min_length=1)
100131
encoding: Literal["proto", "json"] = Field(default="proto")
101132
service_type: Literal["grpc", "http", "jsonrpc"]
102-
service_api_source: Optional[str] = Field(min_length=1, default=None, init=False)
133+
service_api_source: Optional[str] = Field(default=None, init=False)
103134
model_ipfs_hash: Optional[str] = Field(min_length=1, default=None, init=False, deprecated=True)
104-
mpe_address: str
105-
groups: list[Group]
106-
service_description: ServiceDescription
107-
media: list[Media]
108-
contributors: list[Contributor]
109-
tags: list[str]
135+
mpe_address: Optional[str] = Field(default=None, init=False)
136+
groups: list[Group] = Field(default=[])
137+
service_description: Optional[ServiceDescription] = Field(default=None)
138+
media: list[Media] = Field(default=[])
139+
contributors: list[Contributor] = Field(default=[])
140+
tags: list[str] = Field(default=[])
110141

111142
@model_validator(mode="before")
112143
@classmethod
@@ -117,7 +148,6 @@ def restrict_deprecated_fields(cls, data: Any, info: ValidationInfo) -> Any:
117148
is_fetching = info.context and info.context.get("from_storage") is True
118149

119150
if not is_fetching and data.get("model_ipfs_hash"):
120-
# TODO: configure exceptions
121151
raise ValueError(
122152
"The 'model_ipfs_hash' field is deprecated and cannot be used "
123153
"to create new metadata. Please use 'service_api_source' instead."
@@ -128,10 +158,22 @@ def restrict_deprecated_fields(cls, data: Any, info: ValidationInfo) -> Any:
128158
def generate_final_json(self):
129159
if self.service_api_source is None:
130160
if self.model_ipfs_hash is None:
131-
# TODO: configure exceptions
132161
raise ValueError("The 'service_api_source' field is missing!")
133162
else:
134-
self.service_api_source, self.model_ipfs_hash = self.model_ipfs_hash, None
163+
self.service_api_source = FileURI.normalize_string_uri(self.model_ipfs_hash)
164+
self.model_ipfs_hash = None
165+
166+
if not self.mpe_address:
167+
raise ValueError("The 'mpe_address' field is missing!")
168+
169+
if len(self.groups) == 0:
170+
raise ValueError("There must be one item in 'groups' field at least!")
171+
172+
if len(self.contributors) == 0:
173+
raise ValueError("There must be one item in 'contributors' field at least!")
174+
175+
if not self.service_description:
176+
raise ValueError("The 'mpe_address' field is missing!")
135177

136178
return self.model_dump_json(indent=2, exclude_none=True)
137179

0 commit comments

Comments
 (0)