Skip to content

Commit 9302992

Browse files
committed
UsersRepo.new_user
1 parent 136f5de commit 9302992

File tree

4 files changed

+52
-34
lines changed

4 files changed

+52
-34
lines changed

packages/postgres-database/src/simcore_postgres_database/utils_users.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
import sqlalchemy as sa
1212
from common_library.async_tools import maybe_await
1313
from sqlalchemy import Column
14+
from sqlalchemy.exc import IntegrityError
15+
from sqlalchemy.ext.asyncio.engine import AsyncConnection, AsyncEngine
1416

1517
from ._protocols import DBConnection
16-
from .aiopg_errors import UniqueViolation
1718
from .models.users import UserRole, UserStatus, users
1819
from .models.users_details import users_pre_registration_details
20+
from .utils_repos import pass_or_acquire_connection, transaction_context
1921

2022

2123
class BaseUserRepoError(Exception):
@@ -53,9 +55,12 @@ def generate_alternative_username(username: str) -> str:
5355

5456

5557
class UsersRepo:
58+
5659
@staticmethod
5760
async def new_user(
58-
conn: DBConnection,
61+
engine: AsyncEngine,
62+
connection: AsyncConnection | None = None,
63+
*,
5964
email: str,
6065
password_hash: str,
6166
status: UserStatus,
@@ -73,26 +78,28 @@ async def new_user(
7378
user_id = None
7479
while user_id is None:
7580
try:
76-
user_id = await conn.scalar(
77-
users.insert().values(**data).returning(users.c.id)
78-
)
79-
except UniqueViolation:
81+
async with transaction_context(engine, connection) as conn:
82+
user_id = await conn.scalar(
83+
users.insert().values(**data).returning(users.c.id)
84+
)
85+
except IntegrityError:
8086
data["name"] = generate_alternative_username(data["name"])
8187

82-
result = await conn.execute(
83-
sa.select(
84-
users.c.id,
85-
users.c.name,
86-
users.c.email,
87-
users.c.role,
88-
users.c.status,
89-
).where(users.c.id == user_id)
90-
)
91-
return await maybe_await(result.first())
88+
async with pass_or_acquire_connection(engine, connection) as conn:
89+
result = await conn.execute(
90+
sa.select(
91+
users.c.id,
92+
users.c.name,
93+
users.c.email,
94+
users.c.role,
95+
users.c.status,
96+
).where(users.c.id == user_id)
97+
)
98+
return result.one()
9299

93100
@staticmethod
94101
async def link_and_update_user_from_pre_registration(
95-
conn: DBConnection,
102+
connection: AsyncConnection,
96103
*,
97104
new_user_id: int,
98105
new_user_email: str,
@@ -107,7 +114,7 @@ async def link_and_update_user_from_pre_registration(
107114
assert new_user_id > 0 # nosec
108115

109116
# link both tables first
110-
result = await conn.execute(
117+
result = await connection.execute(
111118
users_pre_registration_details.update()
112119
.where(users_pre_registration_details.c.pre_email == new_user_email)
113120
.values(user_id=new_user_id)
@@ -133,14 +140,14 @@ async def link_and_update_user_from_pre_registration(
133140
and c.name.startswith("pre_")
134141
}, "Different pre-cols detected. This code might need an update update"
135142

136-
result = await conn.execute(
143+
result = await connection.execute(
137144
sa.select(*pre_columns).where(
138145
users_pre_registration_details.c.pre_email == new_user_email
139146
)
140147
)
141148
if pre_registration_details_data := result.first():
142149
# NOTE: could have many products! which to use?
143-
await conn.execute(
150+
await connection.execute(
144151
users.update()
145152
.where(users.c.id == new_user_id)
146153
.values(

packages/postgres-database/tests/test_users.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111
from pytest_simcore.helpers.faker_factories import random_user
1212
from simcore_postgres_database.models.users import UserRole, UserStatus, users
1313
from simcore_postgres_database.utils_repos import (
14+
pass_or_acquire_connection,
1415
transaction_context,
1516
)
1617
from simcore_postgres_database.utils_users import (
1718
UsersRepo,
1819
_generate_username_from_email,
1920
generate_alternative_username,
2021
)
21-
from sqlalchemy.exc import DataError, IntegrityError
22+
from sqlalchemy.exc import DBAPIError, IntegrityError
2223
from sqlalchemy.ext.asyncio import AsyncEngine
2324
from sqlalchemy.sql import func
2425

@@ -52,7 +53,7 @@ async def test_user_status_as_pending(
5253
data = random_user(faker, status="PENDING")
5354
assert data["status"] == "PENDING"
5455
async with transaction_context(asyncpg_engine) as connection:
55-
with pytest.raises(DataError) as err_info:
56+
with pytest.raises(DBAPIError) as err_info:
5657
await connection.execute(users.insert().values(data))
5758

5859
assert (
@@ -113,16 +114,20 @@ async def test_unique_username(
113114
assert user.id == user_id
114115
assert user.name == "pcrespov"
115116

117+
async with transaction_context(asyncpg_engine) as connection:
116118
# same name fails
117119
data["email"] = faker.email()
118120
with pytest.raises(IntegrityError):
119121
await connection.scalar(users.insert().values(data).returning(users.c.id))
120122

123+
async with transaction_context(asyncpg_engine) as connection:
121124
# generate new name
122125
data["name"] = _generate_username_from_email(user.email)
123126
data["email"] = faker.email()
124127
await connection.scalar(users.insert().values(data).returning(users.c.id))
125128

129+
async with transaction_context(asyncpg_engine) as connection:
130+
126131
# and another one
127132
data["name"] = generate_alternative_username(data["name"])
128133
data["email"] = faker.email()
@@ -138,21 +143,21 @@ async def test_new_user(
138143
"status": UserStatus.ACTIVE,
139144
"expires_at": datetime.utcnow(),
140145
}
141-
async with transaction_context(asyncpg_engine) as connection:
142-
new_user = await UsersRepo.new_user(connection, **data)
146+
new_user = await UsersRepo.new_user(asyncpg_engine, **data)
143147

144-
assert new_user.email == data["email"]
145-
assert new_user.status == data["status"]
146-
assert new_user.role == UserRole.USER
148+
assert new_user.email == data["email"]
149+
assert new_user.status == data["status"]
150+
assert new_user.role == UserRole.USER
147151

148-
other_email = f"{new_user.name}@other-domain.com"
149-
assert _generate_username_from_email(other_email) == new_user.name
150-
other_data = {**data, "email": other_email}
152+
other_email = f"{new_user.name}@other-domain.com"
153+
assert _generate_username_from_email(other_email) == new_user.name
154+
other_data = {**data, "email": other_email}
151155

152-
other_user = await UsersRepo.new_user(connection, **other_data)
153-
assert other_user.email != new_user.email
154-
assert other_user.name != new_user.name
156+
other_user = await UsersRepo.new_user(asyncpg_engine, **other_data)
157+
assert other_user.email != new_user.email
158+
assert other_user.name != new_user.name
155159

160+
async with pass_or_acquire_connection(asyncpg_engine) as connection:
156161
assert await UsersRepo.get_email(connection, other_user.id) == other_user.email
157162
assert await UsersRepo.get_role(connection, other_user.id) == other_user.role
158163
assert (

packages/postgres-database/tests/test_users_details.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ async def test_create_and_link_user_from_pre_registration(
258258
async with transaction_context(asyncpg_engine) as connection:
259259
# user gets created
260260
new_user = await UsersRepo.new_user(
261+
asyncpg_engine,
261262
connection,
262263
email=pre_email,
263264
password_hash="123456", # noqa: S106
@@ -292,6 +293,7 @@ async def test_get_billing_details_from_pre_registration(
292293
# Create the user
293294
async with transaction_context(asyncpg_engine) as connection:
294295
new_user = await UsersRepo.new_user(
296+
asyncpg_engine,
295297
connection,
296298
email=pre_email,
297299
password_hash="123456", # noqa: S106
@@ -332,6 +334,7 @@ async def test_update_user_from_pre_registration(
332334
# Create the user and link to pre-registration
333335
async with transaction_context(asyncpg_engine) as connection:
334336
new_user = await UsersRepo.new_user(
337+
asyncpg_engine,
335338
connection,
336339
email=pre_email,
337340
password_hash="123456", # noqa: S106
@@ -490,6 +493,7 @@ async def test_user_preregisters_for_multiple_products_with_different_outcomes(
490493
# 3.Now create a user account with the approved pre-registration
491494
async with transaction_context(asyncpg_engine) as connection:
492495
new_user = await UsersRepo.new_user(
496+
asyncpg_engine,
493497
connection,
494498
email=user_email,
495499
password_hash="123456", # noqa: S106

services/web/server/src/simcore_service_webserver/login/_auth_service.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ async def create_user(
3131
expires_at: datetime | None,
3232
) -> dict[str, Any]:
3333

34-
async with transaction_context(get_asyncpg_engine(app)) as conn:
34+
asyncpg_engine = get_asyncpg_engine(app)
35+
async with transaction_context(asyncpg_engine) as conn:
3536
user = await UsersRepo.new_user(
37+
asyncpg_engine,
3638
conn,
3739
email=email,
3840
password_hash=security_service.encrypt_password(password),

0 commit comments

Comments
 (0)