Skip to content

Commit 97b69ef

Browse files
committed
✨ Refactor changelog handling in API routes and add tests for route configuration
1 parent 4cf8ef1 commit 97b69ef

File tree

7 files changed

+239
-50
lines changed

7 files changed

+239
-50
lines changed

services/api-server/src/simcore_service_api_server/api/routes/_constants.py

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Final
1+
from typing import Any, Final
22

33
#
44
# CHANGELOG formatted-messages for API routes
@@ -9,19 +9,16 @@
99
# - Inspired on this idea https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#describing-changes-between-versions
1010
#
1111

12-
# new routes
12+
# newly created endpoint in given version
1313
FMSG_CHANGELOG_NEW_IN_VERSION: Final[str] = "New in *version {}*\n"
1414

15-
# new inputs/outputs in routes
16-
FMSG_CHANGELOG_ADDED_IN_VERSION: Final[str] = "Added in *version {}*: {}\n"
17-
18-
# changes on inputs/outputs in routes
15+
# changes in given version with message
1916
FMSG_CHANGELOG_CHANGED_IN_VERSION: Final[str] = "Changed in *version {}*: {}\n"
2017

21-
# removed on inputs/outputs in routes
22-
FMSG_CHANGELOG_REMOVED_IN_VERSION_FORMAT: Final[str] = "Removed in *version {}*: {}\n"
18+
# marked as deprecated and will be removed in given version
19+
FMSG_CHANGELOG_REMOVED_IN_VERSION: Final[str] = "Removed in *version {}*: {}\n"
2320

24-
FMSG_DEPRECATED_ROUTE_NOTICE: Final[str] = (
21+
FMSG_CHANGELOG_DEPRECATED: Final[str] = (
2522
"🚨 **Deprecated**: This endpoint is deprecated and will be removed in a future release.\n"
2623
"Please use `{}` instead.\n\n"
2724
)
@@ -33,8 +30,8 @@ def create_route_description(
3330
*,
3431
base: str = "",
3532
deprecated: bool = False,
36-
alternative: str | None = None, # alternative
37-
changelog: list[str] | None = None
33+
alternative: str | None = None, # alternative route to use
34+
changelog: list[str] | None = None,
3835
) -> str:
3936
"""
4037
Builds a consistent route description with optional deprecation and changelog information.
@@ -51,7 +48,7 @@ def create_route_description(
5148

5249
if deprecated:
5350
assert alternative, "If deprecated, alternative must be provided" # nosec
54-
parts.append(FMSG_DEPRECATED_ROUTE_NOTICE.format(alternative))
51+
parts.append(FMSG_CHANGELOG_DEPRECATED.format(alternative))
5552

5653
if base:
5754
parts.append(base)
@@ -60,3 +57,68 @@ def create_route_description(
6057
parts.append("\n".join(changelog))
6158

6259
return "\n\n".join(parts)
60+
61+
62+
def create_route_config(
63+
base_description: str = "",
64+
*,
65+
to_be_released_in: str | None = None,
66+
to_be_removed_in: str | None = None,
67+
alternative: str | None = None,
68+
changelog: list[str] | None = None,
69+
) -> dict[str, Any]:
70+
"""
71+
Creates route configuration options including description.
72+
73+
Args:
74+
base_description: Main route description
75+
to_be_released_in: Version where this route will be released (for upcoming features)
76+
to_be_removed_in: Version where this route will be removed (for deprecated endpoints)
77+
alternative: Alternative route to use (required if to_be_removed_in is provided)
78+
changelog: List of formatted changelog strings
79+
80+
Returns:
81+
dict: Route configuration options that can be used as kwargs for route decorators
82+
"""
83+
route_options: dict[str, Any] = {}
84+
85+
# Build changelog
86+
if changelog is None:
87+
changelog = []
88+
89+
if to_be_released_in:
90+
# Add version information to the changelog if not already there
91+
version_note = FMSG_CHANGELOG_NEW_IN_VERSION.format(to_be_released_in)
92+
if not any(version_note in item for item in changelog):
93+
changelog.append(version_note)
94+
# Hide from schema until released
95+
route_options["include_in_schema"] = False
96+
97+
if to_be_removed_in:
98+
# Require an alternative route if this endpoint will be removed
99+
assert ( # nosec
100+
alternative
101+
), "If to_be_removed_in is provided, alternative must be provided" # nosec
102+
103+
# Add removal notice to the changelog if not already there
104+
removal_message = (
105+
"This endpoint is deprecated and will be removed in a future version"
106+
)
107+
removal_note = FMSG_CHANGELOG_REMOVED_IN_VERSION.format(
108+
to_be_removed_in, removal_message
109+
)
110+
if not any(removal_note in item for item in changelog):
111+
changelog.append(removal_note)
112+
113+
# Mark as deprecated
114+
route_options["deprecated"] = True
115+
116+
# Create description
117+
route_options["description"] = create_route_description(
118+
base=base_description,
119+
deprecated=bool(to_be_removed_in),
120+
alternative=alternative,
121+
changelog=changelog,
122+
)
123+
124+
return route_options

services/api-server/src/simcore_service_api_server/api/routes/files.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@
3030
)
3131
from simcore_sdk.node_ports_common.filemanager import upload_path as storage_upload_path
3232
from simcore_service_api_server.api.routes._constants import (
33-
FMSG_CHANGELOG_ADDED_IN_VERSION,
34-
FMSG_CHANGELOG_REMOVED_IN_VERSION_FORMAT,
35-
create_route_description,
33+
FMSG_CHANGELOG_NEW_IN_VERSION,
34+
create_route_config,
3635
)
3736
from starlette.datastructures import URL
3837
from starlette.responses import RedirectResponse
@@ -141,16 +140,12 @@ async def _create_domain_file(
141140
"",
142141
response_model=list[OutputFile],
143142
responses=_FILE_STATUS_CODES,
144-
description=create_route_description(
145-
base="Lists all files stored in the system",
146-
deprecated=True,
143+
**create_route_config(
144+
base_description="Lists all files stored in the system",
145+
to_be_removed_in="0.7",
147146
alternative="GET /v0/files/page",
148147
changelog=[
149-
FMSG_CHANGELOG_ADDED_IN_VERSION.format("0.5", ""),
150-
FMSG_CHANGELOG_REMOVED_IN_VERSION_FORMAT.format(
151-
"0.7",
152-
"This endpoint is deprecated and will be removed in a future version",
153-
),
148+
FMSG_CHANGELOG_NEW_IN_VERSION.format("0.5"),
154149
],
155150
),
156151
)

services/api-server/src/simcore_service_api_server/api/routes/programs.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
from ..._service_programs import ProgramService
2424
from ...api.routes._constants import (
2525
DEFAULT_MAX_STRING_LENGTH,
26-
FMSG_CHANGELOG_NEW_IN_VERSION,
27-
create_route_description,
26+
create_route_config,
2827
)
2928
from ...models.basic_types import VersionStr
3029
from ...models.pagination import Page, PaginationParams
@@ -39,13 +38,10 @@
3938
@router.get(
4039
"",
4140
response_model=Page[Program],
42-
description=create_route_description(
43-
base="Lists the latest of all available programs",
44-
changelog=[
45-
FMSG_CHANGELOG_NEW_IN_VERSION.format("0.8"),
46-
],
41+
**create_route_config(
42+
base_description="Lists the latest of all available programs",
43+
to_be_released_in="0.8",
4744
),
48-
include_in_schema=False, # TO BE RELEASED in 0.8
4945
)
5046
async def list_programs(
5147
user_id: Annotated[PositiveInt, Depends(get_current_user_id)],
@@ -78,13 +74,10 @@ async def list_programs(
7874
@router.get(
7975
"/{program_key:path}/releases",
8076
response_model=Page[Program],
81-
description=create_route_description(
82-
base="Lists the latest of all available programs",
83-
changelog=[
84-
FMSG_CHANGELOG_NEW_IN_VERSION.format("0.8"),
85-
],
77+
**create_route_config(
78+
base_description="Lists the latest of all available programs",
79+
to_be_released_in="0.8",
8680
),
87-
include_in_schema=False, # TO BE RELEASED in 0.8
8881
)
8982
async def list_program_history(
9083
program_key: ProgramKeyId,

services/api-server/src/simcore_service_api_server/api/routes/solvers.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
from ..dependencies.webserver_http import AuthSession, get_webserver_session
2727
from ._constants import (
2828
FMSG_CHANGELOG_NEW_IN_VERSION,
29-
FMSG_CHANGELOG_REMOVED_IN_VERSION_FORMAT,
29+
FMSG_CHANGELOG_REMOVED_IN_VERSION,
30+
create_route_config,
3031
create_route_description,
3132
)
3233

@@ -59,13 +60,13 @@
5960
"",
6061
response_model=list[Solver],
6162
responses=_SOLVER_STATUS_CODES,
62-
description=create_route_description(
63-
base="Lists all available solvers (latest version)",
64-
deprecated=True,
63+
**create_route_config(
64+
base_description="Lists all available solvers (latest version)",
65+
to_be_removed_in="0.7",
6566
alternative="GET /v0/solvers/page",
6667
changelog=[
6768
FMSG_CHANGELOG_NEW_IN_VERSION.format("0.5.0", ""),
68-
FMSG_CHANGELOG_REMOVED_IN_VERSION_FORMAT.format(
69+
FMSG_CHANGELOG_REMOVED_IN_VERSION.format(
6970
"0.7",
7071
"This endpoint is deprecated and will be removed in a future version",
7172
),
@@ -141,7 +142,7 @@ async def get_solvers_page(
141142
alternative="GET /v0/solvers/{solver_key}/releases/page",
142143
changelog=[
143144
FMSG_CHANGELOG_NEW_IN_VERSION.format("0.5.0", ""),
144-
FMSG_CHANGELOG_REMOVED_IN_VERSION_FORMAT.format(
145+
FMSG_CHANGELOG_REMOVED_IN_VERSION.format(
145146
"0.7",
146147
"This endpoint is deprecated and will be removed in a future version",
147148
),

services/api-server/src/simcore_service_api_server/api/routes/solvers_jobs.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
from ..dependencies.services import get_api_client, get_solver_service
3838
from ..dependencies.webserver_http import AuthSession, get_webserver_session
3939
from ._constants import (
40-
FMSG_CHANGELOG_ADDED_IN_VERSION,
4140
FMSG_CHANGELOG_CHANGED_IN_VERSION,
4241
FMSG_CHANGELOG_NEW_IN_VERSION,
4342
)
@@ -164,8 +163,10 @@ async def delete_job(
164163
},
165164
},
166165
description="Starts job job_id created with the solver solver_key:version\n\n"
167-
+ FMSG_CHANGELOG_ADDED_IN_VERSION.format("0.4.3", "query parameter `cluster_id`")
168-
+ FMSG_CHANGELOG_ADDED_IN_VERSION.format(
166+
+ FMSG_CHANGELOG_CHANGED_IN_VERSION.format(
167+
"0.4.3", "adds query parameter `cluster_id`"
168+
)
169+
+ FMSG_CHANGELOG_CHANGED_IN_VERSION.format(
169170
"0.6", "responds with a 202 when successfully starting a computation"
170171
)
171172
+ FMSG_CHANGELOG_CHANGED_IN_VERSION.format(

services/api-server/src/simcore_service_api_server/api/routes/solvers_jobs_getters.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@
5959
from ..dependencies.webserver_http import AuthSession, get_webserver_session
6060
from ._constants import (
6161
FMSG_CHANGELOG_NEW_IN_VERSION,
62-
FMSG_CHANGELOG_REMOVED_IN_VERSION_FORMAT,
62+
FMSG_CHANGELOG_REMOVED_IN_VERSION,
63+
create_route_config,
6364
create_route_description,
6465
)
6566
from .solvers_jobs import (
@@ -192,13 +193,13 @@ async def list_all_solvers_jobs(
192193
"/{solver_key:path}/releases/{version}/jobs",
193194
response_model=list[Job],
194195
responses=JOBS_STATUS_CODES,
195-
description=create_route_description(
196-
base="List of jobs in a specific released solver",
196+
**create_route_config(
197+
base_description="List of jobs in a specific released solver (limited to 20 jobs)",
197198
deprecated=True,
198199
alternative="GET /{solver_key}/releases/{version}/jobs/page",
199200
changelog=[
200201
FMSG_CHANGELOG_NEW_IN_VERSION.format("0.5"),
201-
FMSG_CHANGELOG_REMOVED_IN_VERSION_FORMAT.format(
202+
FMSG_CHANGELOG_REMOVED_IN_VERSION.format(
202203
"0.7",
203204
"This endpoint is deprecated and will be removed in a future version",
204205
),

0 commit comments

Comments
 (0)