Skip to content

Commit 53581e1

Browse files
committed
refactor common code in programs and solvers
1 parent bf7c51c commit 53581e1

File tree

3 files changed

+40
-76
lines changed

3 files changed

+40
-76
lines changed

services/api-server/src/simcore_service_api_server/models/schemas/programs.py

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from models_library.services import ServiceMetaDataPublished
66
from models_library.services_regex import DYNAMIC_SERVICE_KEY_RE
77
from packaging.version import Version
8-
from pydantic import ConfigDict, Field, HttpUrl, StringConstraints
8+
from pydantic import BaseModel, ConfigDict, Field, HttpUrl, StringConstraints
99
from simcore_service_api_server.models.schemas._utils import ApiServerOutputSchema
1010

1111
from ...models._utils_pydantic import UriSchema
@@ -27,12 +27,10 @@
2727
]
2828

2929

30-
class Program(ApiServerOutputSchema):
31-
"""A released solver with a specific version"""
32-
33-
id: Annotated[ProgramKeyId, Field(..., description="Program identifier")]
30+
class BaseResource(BaseModel):
31+
id: Annotated[str, Field(..., description="Resource identifier")]
3432
version: Annotated[
35-
VersionStr, Field(..., description="semantic version number of the node")
33+
VersionStr, Field(..., description="Semantic version number of the resource")
3634
]
3735
title: Annotated[
3836
str,
@@ -42,12 +40,40 @@ class Program(ApiServerOutputSchema):
4240
description: Annotated[
4341
str | None,
4442
StringConstraints(max_length=500),
45-
Field(None, description="Description of the program"),
43+
Field(default=None, description="Description of the resource"),
4644
]
4745
url: Annotated[
4846
HttpUrl | None, UriSchema(), Field(..., description="Link to get this resource")
4947
]
5048

49+
@property
50+
def pep404_version(self) -> Version:
51+
"""Rich version type that can be used e.g. to compare"""
52+
return packaging.version.parse(self.version)
53+
54+
@property
55+
def url_friendly_id(self) -> str:
56+
"""Use to pass id as parameter in URLs"""
57+
return urllib.parse.quote_plus(self.id)
58+
59+
@property
60+
def resource_name(self) -> str:
61+
"""Relative resource name"""
62+
return self.compose_resource_name(self.id, self.version)
63+
64+
@property
65+
def name(self) -> str:
66+
"""API standards notation (see api_resources.py)"""
67+
return self.resource_name
68+
69+
@classmethod
70+
def compose_resource_name(cls, key: str, version: str) -> str:
71+
raise NotImplementedError("Subclasses must implement this method")
72+
73+
74+
class Program(BaseResource, ApiServerOutputSchema):
75+
"""A released program with a specific version"""
76+
5177
model_config = ConfigDict(
5278
extra="ignore",
5379
json_schema_extra={
@@ -67,7 +93,6 @@ def create_from_image(cls, image_meta: ServiceMetaDataPublished) -> "Program":
6793
data = image_meta.model_dump(
6894
include={"name", "key", "version", "description", "contact"},
6995
)
70-
7196
return cls(
7297
id=data.pop("key"),
7398
version=data.pop("version"),
@@ -76,26 +101,6 @@ def create_from_image(cls, image_meta: ServiceMetaDataPublished) -> "Program":
76101
**data,
77102
)
78103

79-
@property
80-
def pep404_version(self) -> Version:
81-
"""Rich version type that can be used e.g. to compare"""
82-
return packaging.version.parse(self.version)
83-
84-
@property
85-
def url_friendly_id(self) -> str:
86-
"""Use to pass id as parameter in urls"""
87-
return urllib.parse.quote_plus(self.id)
88-
89-
@property
90-
def resource_name(self) -> str:
91-
"""Relative resource name"""
92-
return self.compose_resource_name(self.id, self.version)
93-
94-
@property
95-
def name(self) -> str:
96-
"""API standards notation (see api_resources.py)"""
97-
return self.resource_name
98-
99104
@classmethod
100105
def compose_resource_name(
101106
cls, program_key: ProgramKeyId, program_version: VersionStr

services/api-server/src/simcore_service_api_server/models/schemas/solvers.py

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1-
import urllib.parse
21
from typing import Annotated, Any, Literal
32

4-
import packaging.version
53
from models_library.basic_regex import PUBLIC_VARIABLE_NAME_RE
64
from models_library.services import ServiceMetaDataPublished
75
from models_library.services_regex import COMPUTATIONAL_SERVICE_KEY_RE
8-
from packaging.version import Version
9-
from pydantic import BaseModel, ConfigDict, Field, HttpUrl, StringConstraints
6+
from pydantic import BaseModel, ConfigDict, Field, StringConstraints
107

11-
from ...models._utils_pydantic import UriSchema
8+
from ...models.schemas.programs import BaseResource
129
from ..api_resources import compose_resource_name
13-
from ..basic_types import VersionStr
1410

1511
# NOTE:
1612
# - API does NOT impose prefix (simcore)/(services)/comp because does not know anything about registry deployed. This constraint
@@ -36,27 +32,10 @@
3632
]
3733

3834

39-
class Solver(BaseModel):
35+
class Solver(BaseResource):
4036
"""A released solver with a specific version"""
4137

42-
id: SolverKeyId = Field(..., description="Solver identifier")
43-
version: VersionStr = Field(
44-
...,
45-
description="semantic version number of the node",
46-
)
47-
48-
# Human readables Identifiers
49-
title: str = Field(..., description="Human readable name")
50-
description: str | None = None
51-
maintainer: str
52-
# TODO: consider released: Optional[datetime] required?
53-
# TODO: consider version_aliases: list[str] = [] # remaining tags
54-
55-
# Get links to other resources
56-
url: Annotated[
57-
Annotated[HttpUrl, UriSchema()] | None,
58-
Field(..., description="Link to get this resource"),
59-
]
38+
maintainer: str = Field(..., description="Maintainer of the solver")
6039

6140
model_config = ConfigDict(
6241
extra="ignore",
@@ -77,7 +56,6 @@ def create_from_image(cls, image_meta: ServiceMetaDataPublished) -> "Solver":
7756
data = image_meta.model_dump(
7857
include={"name", "key", "version", "description", "contact"},
7958
)
80-
8159
return cls(
8260
id=data.pop("key"),
8361
version=data.pop("version"),
@@ -87,28 +65,8 @@ def create_from_image(cls, image_meta: ServiceMetaDataPublished) -> "Solver":
8765
**data,
8866
)
8967

90-
@property
91-
def pep404_version(self) -> Version:
92-
"""Rich version type that can be used e.g. to compare"""
93-
return packaging.version.parse(self.version)
94-
95-
@property
96-
def url_friendly_id(self) -> str:
97-
"""Use to pass id as parameter in urls"""
98-
return urllib.parse.quote_plus(self.id)
99-
100-
@property
101-
def resource_name(self) -> str:
102-
"""Relative resource name"""
103-
return self.compose_resource_name(self.id, self.version)
104-
105-
@property
106-
def name(self) -> str:
107-
"""API standards notation (see api_resources.py)"""
108-
return self.resource_name
109-
11068
@classmethod
111-
def compose_resource_name(cls, solver_key, solver_version) -> str:
69+
def compose_resource_name(cls, solver_key: str, solver_version: str) -> str:
11270
return compose_resource_name("solvers", solver_key, "releases", solver_version)
11371

11472

services/api-server/tests/unit/test_services_solver_job_models_converters.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,9 @@ def fake_url_for(*args, **kwargs) -> HttpUrl:
205205
solver = Solver(
206206
id=solver_key,
207207
version=solver_version,
208-
title=faker.text(),
208+
title=faker.text(max_nb_chars=20),
209209
maintainer=faker.name(),
210+
description=faker.text(max_nb_chars=100),
210211
url=None,
211212
)
212213

0 commit comments

Comments
 (0)