Skip to content

Commit 832c17b

Browse files
author
Andrei Neagu
committed
Merge remote-tracking branch 'upstream/master' into pr-osparc-migrate-dy-scheduler-part4
2 parents 62c4deb + 58f9862 commit 832c17b

File tree

167 files changed

+1653
-1182
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

167 files changed

+1653
-1182
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
mode: edit
3+
description: Converts string-based aiohttp app key constants to type-safe web.AppKey
4+
model: GPT-4.1
5+
---
6+
7+
Convert all string-based app key constants to use type-safe web.AppKey.
8+
9+
- Replace patterns like:
10+
```python
11+
CONSTNAME_APPKEY: Final[str] = f"{__name__}.my_key"
12+
```
13+
with:
14+
```python
15+
from aiohttp import web
16+
CONSTNAME_APPKEY: Final = web.AppKey("CONSTNAME", ValueType)
17+
```
18+
(Replace ValueType with the actual type stored under this key.)
19+
20+
- Update all usages:
21+
- `app[CONSTNAME_APPKEY] = value`
22+
- `data = app[CONSTNAME_APPKEY]` or `data = request.app[CONSTNAME_APPKEY]`
23+
24+
- Key constant MUST be UPPERCASE
25+
- Key name MUST be suffixed `_APPKEY`
26+
- Remove any f"{__name__}..." patterns; use a simple string identifier in web.AppKey.
27+
- Ensure all keys are type-safe and self-documenting.
28+
- IF you change the original name, you MUST change all the references
File renamed without changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""update confirmation created column
2+
3+
Revision ID: 9dddb16914a4
4+
Revises: 06eafd25d004
5+
Create Date: 2025-07-28 17:25:06.534720+00:00
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
from sqlalchemy.dialects import postgresql
12+
13+
# revision identifiers, used by Alembic.
14+
revision = "9dddb16914a4"
15+
down_revision = "7e92447558e0"
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# Step 1: Add new column as nullable first
22+
op.add_column(
23+
"confirmations",
24+
sa.Column(
25+
"created",
26+
sa.DateTime(timezone=True),
27+
nullable=True,
28+
),
29+
)
30+
31+
# Step 2: Copy data from created_at to created, assuming UTC timezone for existing data
32+
op.execute(
33+
"UPDATE confirmations SET created = created_at AT TIME ZONE 'UTC' WHERE created_at IS NOT NULL"
34+
)
35+
36+
# Step 3: Make the column non-nullable with default
37+
op.alter_column(
38+
"confirmations",
39+
"created",
40+
nullable=False,
41+
server_default=sa.text("now()"),
42+
)
43+
44+
# Step 4: Drop old column
45+
op.drop_column("confirmations", "created_at")
46+
47+
48+
def downgrade():
49+
# Step 1: Add back the old column
50+
op.add_column(
51+
"confirmations",
52+
sa.Column(
53+
"created_at", postgresql.TIMESTAMP(), autoincrement=False, nullable=True
54+
),
55+
)
56+
57+
# Step 2: Copy data back, converting timezone-aware to naive timestamp
58+
op.execute(
59+
"UPDATE confirmations SET created_at = created AT TIME ZONE 'UTC' WHERE created IS NOT NULL"
60+
)
61+
62+
# Step 3: Make the column non-nullable
63+
op.alter_column(
64+
"confirmations",
65+
"created_at",
66+
nullable=False,
67+
)
68+
69+
# Step 4: Drop new column
70+
op.drop_column("confirmations", "created")

packages/postgres-database/src/simcore_postgres_database/models/confirmations.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
""" User's confirmations table
1+
"""User's confirmations table
22
3-
- Keeps a list of tokens to identify an action (registration, invitation, reset, etc) authorized
4-
by link to a a user in the framework
5-
- These tokens have an expiration date defined by configuration
3+
- Keeps a list of tokens to identify an action (registration, invitation, reset, etc) authorized
4+
by link to a a user in the framework
5+
- These tokens have an expiration date defined by configuration
66
77
"""
8+
89
import enum
910

1011
import sqlalchemy as sa
1112

12-
from ._common import RefActions
13+
from ._common import RefActions, column_created_datetime
1314
from .base import metadata
1415
from .users import users
1516

@@ -47,12 +48,8 @@ class ConfirmationAction(enum.Enum):
4748
sa.Text,
4849
doc="Extra data associated to the action. SEE handlers_confirmation.py::email_confirmation",
4950
),
50-
sa.Column(
51-
"created_at",
52-
sa.DateTime(),
53-
nullable=False,
54-
# NOTE: that here it would be convenient to have a server_default=now()!
55-
doc="Creation date of this code."
51+
column_created_datetime(
52+
doc="Creation date of this code. "
5653
"Can be used as reference to determine the expiration date. SEE ${ACTION}_CONFIRMATION_LIFETIME",
5754
),
5855
# constraints ----------------

packages/pytest-simcore/src/pytest_simcore/helpers/webserver_login.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55

66
from aiohttp.test_utils import TestClient
77
from servicelib.aiohttp import status
8-
from simcore_service_webserver.login._invitations_service import create_invitation_token
9-
from simcore_service_webserver.login._login_repository_legacy import (
10-
get_plugin_storage,
8+
from simcore_service_webserver.login._controller.rest._rest_dependencies import (
9+
get_confirmation_service,
1110
)
11+
from simcore_service_webserver.login._invitations_service import create_invitation_token
1212
from simcore_service_webserver.login.constants import MSG_LOGGED_IN
1313
from simcore_service_webserver.security import security_service
1414
from yarl import URL
@@ -132,15 +132,15 @@ def __init__(
132132
self.confirmation = None
133133
self.trial_days = trial_days
134134
self.extra_credits_in_usd = extra_credits_in_usd
135-
self.db = get_plugin_storage(self.app)
135+
self.confirmation_service = get_confirmation_service(client.app)
136136

137137
async def __aenter__(self) -> "NewInvitation":
138138
# creates host user
139139
assert self.client.app
140140
self.user = await super().__aenter__()
141141

142142
self.confirmation = await create_invitation_token(
143-
self.db,
143+
self.client.app,
144144
user_id=self.user["id"],
145145
user_email=self.user["email"],
146146
tag=self.tag,
@@ -150,5 +150,11 @@ async def __aenter__(self) -> "NewInvitation":
150150
return self
151151

152152
async def __aexit__(self, *args):
153-
if await self.db.get_confirmation(self.confirmation):
154-
await self.db.delete_confirmation(self.confirmation)
153+
if self.confirmation:
154+
# Try to get confirmation by filter and delete if it exists
155+
confirmation = await self.confirmation_service.get_confirmation(
156+
filter_dict={"code": self.confirmation["code"]}
157+
)
158+
if confirmation:
159+
await self.confirmation_service.delete_confirmation(confirmation)
160+
return await super().__aexit__(*args)

packages/pytest-simcore/src/pytest_simcore/helpers/webserver_projects.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
update_or_insert_project_group,
1818
)
1919
from simcore_service_webserver.projects._projects_repository_legacy import (
20-
APP_PROJECT_DBAPI,
20+
PROJECT_DBAPI_APPKEY,
2121
ProjectDBAPI,
2222
)
2323
from simcore_service_webserver.projects._projects_repository_legacy_utils import (
@@ -71,7 +71,7 @@ async def create_project(
7171

7272
project_data.update(params_override)
7373

74-
db: ProjectDBAPI = app[APP_PROJECT_DBAPI]
74+
db: ProjectDBAPI = app[PROJECT_DBAPI_APPKEY]
7575

7676
new_project = await db.insert_project(
7777
project_data,
@@ -131,7 +131,7 @@ async def create_project(
131131
async def delete_all_projects(app: web.Application):
132132
from simcore_postgres_database.webserver_models import projects
133133

134-
db = app[APP_PROJECT_DBAPI]
134+
db = app[PROJECT_DBAPI_APPKEY]
135135
async with db.engine.acquire() as conn:
136136
query = projects.delete()
137137
await conn.execute(query)

packages/service-library/src/servicelib/aiohttp/application.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ async def _first_call_on_cleanup(app: web.Application):
2323

2424

2525
async def _cancel_all_fire_and_forget_registered_tasks(app: web.Application):
26-
registered_tasks: set[asyncio.Task] = app[APP_FIRE_AND_FORGET_TASKS_KEY]
26+
registered_tasks = app[APP_FIRE_AND_FORGET_TASKS_KEY]
2727
for task in registered_tasks:
2828
task.cancel()
2929

@@ -37,7 +37,7 @@ async def _cancel_all_fire_and_forget_registered_tasks(app: web.Application):
3737
"Following observation tasks completed with an unexpected error:%s",
3838
f"{bad_results}",
3939
)
40-
except asyncio.TimeoutError:
40+
except TimeoutError:
4141
_logger.exception(
4242
"Timed-out waiting more than %s secs for %s to complete. Action: Check why this is blocking",
4343
_MAX_WAIT_TIME_TO_CANCEL_SECONDS,

packages/service-library/src/servicelib/aiohttp/application_keys.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
See https://aiohttp.readthedocs.io/en/stable/web_advanced.html#data-sharing-aka-no-singletons-please
1010
"""
1111

12+
import asyncio
1213
from typing import Final
1314

1415
from aiohttp import ClientSession, web
@@ -21,17 +22,17 @@
2122
#
2223
# web.Application keys, i.e. app[APP_*_KEY]
2324
#
24-
APP_CONFIG_KEY = web.AppKey("APP_CONFIG_KEY", dict[str, object])
25+
APP_CONFIG_KEY: Final = web.AppKey("APP_CONFIG_KEY", dict[str, object])
2526

2627
APP_AIOPG_ENGINE_KEY: Final[str] = f"{__name__ }.aiopg_engine"
2728

28-
APP_CLIENT_SESSION_KEY: web.AppKey[ClientSession] = web.AppKey("APP_CLIENT_SESSION_KEY")
29+
APP_CLIENT_SESSION_KEY: Final = web.AppKey("APP_CLIENT_SESSION_KEY", ClientSession)
2930

3031

31-
APP_FIRE_AND_FORGET_TASKS_KEY: Final[str] = f"{__name__}.tasks"
32+
APP_FIRE_AND_FORGET_TASKS_KEY: Final = web.AppKey(
33+
"APP_FIRE_AND_FORGET_TASKS_KEY", set[asyncio.Task]
34+
)
3235

33-
APP_RABBITMQ_CLIENT_KEY: Final[str] = f"{__name__}.rabbit_client"
34-
APP_RABBITMQ_RPC_SERVER_KEY: Final[str] = f"{__name__}.rabbit_rpc_server"
3536

3637
#
3738
# web.Response keys, i.e. app[RSP_*_KEY]

packages/service-library/src/servicelib/aiohttp/monitor_services.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from enum import Enum
2+
from typing import Final
23

34
from aiohttp import web
45
from prometheus_client import Counter
@@ -22,8 +23,8 @@
2223
#
2324

2425

25-
MONITOR_SERVICE_STARTED = f"{__name__}.services_started"
26-
MONITOR_SERVICE_STOPPED = f"{__name__}.services_stopped"
26+
MONITOR_SERVICE_STARTED_APPKEY: Final = web.AppKey("MONITOR_SERVICE_STARTED", Counter)
27+
MONITOR_SERVICE_STOPPED_APPKEY: Final = web.AppKey("MONITOR_SERVICE_STOPPED", Counter)
2728

2829
MONITOR_SERVICE_STARTED_LABELS: list[str] = [
2930
"service_key",
@@ -42,7 +43,7 @@
4243
def add_instrumentation(
4344
app: web.Application, reg: CollectorRegistry, app_name: str
4445
) -> None:
45-
app[MONITOR_SERVICE_STARTED] = Counter(
46+
app[MONITOR_SERVICE_STARTED_APPKEY] = Counter(
4647
name="services_started_total",
4748
documentation="Counts the services started",
4849
labelnames=MONITOR_SERVICE_STARTED_LABELS,
@@ -51,7 +52,7 @@ def add_instrumentation(
5152
registry=reg,
5253
)
5354

54-
app[MONITOR_SERVICE_STOPPED] = Counter(
55+
app[MONITOR_SERVICE_STOPPED_APPKEY] = Counter(
5556
name="services_stopped_total",
5657
documentation="Counts the services stopped",
5758
labelnames=MONITOR_SERVICE_STOPPED_LABELS,
@@ -73,7 +74,7 @@ def service_started(
7374
service_tag: str,
7475
simcore_user_agent: str,
7576
) -> None:
76-
app[MONITOR_SERVICE_STARTED].labels(
77+
app[MONITOR_SERVICE_STARTED_APPKEY].labels(
7778
service_key=service_key,
7879
service_tag=service_tag,
7980
simcore_user_agent=simcore_user_agent,
@@ -88,7 +89,7 @@ def service_stopped(
8889
simcore_user_agent: str,
8990
result: ServiceResult | str,
9091
) -> None:
91-
app[MONITOR_SERVICE_STOPPED].labels(
92+
app[MONITOR_SERVICE_STOPPED_APPKEY].labels(
9293
service_key=service_key,
9394
service_tag=service_tag,
9495
simcore_user_agent=simcore_user_agent,

packages/service-library/src/servicelib/aiohttp/monitoring.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@
2727

2828
_logger = logging.getLogger(__name__)
2929

30-
_PROMETHEUS_METRICS: Final[str] = f"{__name__}.prometheus_metrics" # noqa: N816
31-
APP_MONITORING_NAMESPACE_KEY: Final = web.AppKey("APP_MONITORING_NAMESPACE_KEY", str)
30+
PROMETHEUS_METRICS_APPKEY: Final = web.AppKey("PROMETHEUS_METRICS", PrometheusMetrics)
31+
MONITORING_NAMESPACE_APPKEY: Final = web.AppKey("APP_MONITORING_NAMESPACE_KEY", str)
3232

3333

3434
def get_collector_registry(app: web.Application) -> CollectorRegistry:
35-
metrics = app[_PROMETHEUS_METRICS]
35+
metrics = app[PROMETHEUS_METRICS_APPKEY]
3636
assert isinstance(metrics, PrometheusMetrics) # nosec
3737
return metrics.registry
3838

@@ -71,7 +71,7 @@ async def middleware_handler(request: web.Request, handler: Handler):
7171
with log_catch(logger=_logger, reraise=False):
7272
await enter_middleware_cb(request)
7373

74-
metrics = request.app[_PROMETHEUS_METRICS]
74+
metrics = request.app[PROMETHEUS_METRICS_APPKEY]
7575
assert isinstance(metrics, PrometheusMetrics) # nosec
7676

7777
user_agent = request.headers.get(
@@ -130,7 +130,7 @@ def setup_monitoring(
130130
enter_middleware_cb: EnterMiddlewareCB | None = None,
131131
exit_middleware_cb: ExitMiddlewareCB | None = None,
132132
):
133-
app[_PROMETHEUS_METRICS] = get_prometheus_metrics()
133+
app[PROMETHEUS_METRICS_APPKEY] = get_prometheus_metrics()
134134

135135
# WARNING: ensure ERROR middleware is over this one
136136
#

0 commit comments

Comments
 (0)