Skip to content
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
a32e396
upgrades tests to asyncpg
pcrespov Jul 17, 2025
7882f21
UsersRepo.new_user
pcrespov Jul 17, 2025
6cb5281
♻️ Refactor UsersRepo to use instance methods for user creation and l…
pcrespov Jul 17, 2025
a842fc7
fixes
pcrespov Jul 17, 2025
308d296
refactor ruther
pcrespov Jul 17, 2025
ad943ba
refactor ruther
pcrespov Jul 17, 2025
cd71407
new users secrests table
pcrespov Jul 17, 2025
4f32540
migration table
pcrespov Jul 17, 2025
8391c31
refactoring users
pcrespov Jul 17, 2025
debb50f
user
pcrespov Jul 17, 2025
b61755b
implement user phone update functionality and refactor related services
pcrespov Jul 17, 2025
fd7d979
update user passwrrod
pcrespov Jul 17, 2025
da4997d
tests migration
pcrespov Jul 17, 2025
d642425
tests migration
pcrespov Jul 17, 2025
1dcc007
create user
pcrespov Jul 17, 2025
6d49cf4
mypy fixes
pcrespov Jul 18, 2025
c34af40
insert_user_and_secrets
pcrespov Jul 18, 2025
f4207bc
fixing tests fixtures
pcrespov Jul 18, 2025
8b0faf5
fixing tests fixtures
pcrespov Jul 18, 2025
e9a4fd5
fixing migration tests
pcrespov Jul 18, 2025
4a41e91
fixes test
pcrespov Jul 18, 2025
77d7161
fixes
pcrespov Jul 18, 2025
c57864d
cleanup
pcrespov Jul 18, 2025
82a28e4
using new techniqe
pcrespov Jul 18, 2025
3ba921c
both user and secrets
pcrespov Jul 18, 2025
fb08c8c
fixing secret
pcrespov Jul 18, 2025
93c5ccf
common
pcrespov Jul 18, 2025
cac9327
rm get_user
pcrespov Jul 18, 2025
134f828
deprecating Storage
pcrespov Jul 18, 2025
5123ab9
refactor: update user injection methods to use new lifespan helpers
pcrespov Jul 18, 2025
772d99a
refactor: improve error handling and user message integration in 2FA …
pcrespov Jul 18, 2025
2e1a775
fixes tests
pcrespov Jul 18, 2025
a0220d6
refactor: update billing details retrieval in UsersRepo and improve l…
pcrespov Jul 18, 2025
efaf9ce
refactor: streamline user and secrets insertion in tests by consolida…
pcrespov Jul 18, 2025
7b62ec7
test: enhance user password hash update tests and add missing assertions
pcrespov Jul 18, 2025
f8f89b1
refactor: ensure user existence check before updating password hash i…
pcrespov Jul 18, 2025
ba4cc5a
refactor: improve user credential retrieval by utilizing UsersRepo an…
pcrespov Jul 18, 2025
d96a9bd
refactor: simplify user creation in tests by utilizing sync_insert_an…
pcrespov Jul 18, 2025
ff9f41b
minor
pcrespov Jul 18, 2025
9aa7514
refactor: fix random_user call in insert_and_get_user_and_secrets_lif…
pcrespov Jul 18, 2025
114e846
fixes tests
pcrespov Jul 21, 2025
6646fa4
rename
pcrespov Jul 21, 2025
6c4e09d
fixes exception
pcrespov Jul 21, 2025
f4f37e9
refactor: update user linking logic to associate all pre-registration…
pcrespov Jul 21, 2025
177de8c
fixes error
pcrespov Jul 21, 2025
72d4f4e
fixes
pcrespov Jul 21, 2025
1d9a6c1
@sanderegg review: relative imports
pcrespov Jul 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 17 additions & 20 deletions packages/notifications-library/tests/with_db/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
from models_library.users import UserID
from notifications_library._templates import get_default_named_templates
from pydantic import validate_call
from pytest_simcore.helpers.postgres_tools import insert_and_get_row_lifespan
from pytest_simcore.helpers.postgres_users import (
insert_and_get_user_and_secrets_lifespan,
)
from simcore_postgres_database.models.jinja2_templates import jinja2_templates
from simcore_postgres_database.models.payments_transactions import payments_transactions
from simcore_postgres_database.models.products import products
from simcore_postgres_database.models.products_to_templates import products_to_templates
from simcore_postgres_database.models.users import users
from sqlalchemy.engine.row import Row
from sqlalchemy.ext.asyncio.engine import AsyncEngine

Expand Down Expand Up @@ -50,16 +53,11 @@ async def user(
and injects a user in db
"""
assert user_id == user["id"]
pk_args = users.c.id, user["id"]

# NOTE: creation of primary group and setting `groupid`` is automatically triggered after creation of user by postgres
async with sqlalchemy_async_engine.begin() as conn:
row: Row = await _insert_and_get_row(conn, users, user, *pk_args)

yield row._asdict()

async with sqlalchemy_async_engine.begin() as conn:
await _delete_row(conn, users, *pk_args)
async with insert_and_get_user_and_secrets_lifespan( # pylint:disable=contextmanager-generator-missing-cleanup
sqlalchemy_async_engine,
**user,
) as row:
yield row


@pytest.fixture
Expand All @@ -82,15 +80,14 @@ async def product(
# NOTE: osparc product is already in db. This is another product
assert product["name"] != "osparc"

pk_args = products.c.name, product["name"]

async with sqlalchemy_async_engine.begin() as conn:
row: Row = await _insert_and_get_row(conn, products, product, *pk_args)

yield row._asdict()

async with sqlalchemy_async_engine.begin() as conn:
await _delete_row(conn, products, *pk_args)
async with insert_and_get_row_lifespan( # pylint:disable=contextmanager-generator-missing-cleanup
sqlalchemy_async_engine,
table=products,
values=product,
pk_col=products.c.name,
pk_value=product["name"],
) as row:
yield row


@pytest.fixture
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""new users secrets

Revision ID: 5679165336c8
Revises: 61b98a60e934
Create Date: 2025-07-17 17:07:20.200038+00:00

"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "5679165336c8"
down_revision = "61b98a60e934"
branch_labels = None
depends_on = None


def upgrade():
op.create_table(
"users_secrets",
sa.Column("user_id", sa.BigInteger(), nullable=False),
sa.Column("password_hash", sa.String(), nullable=False),
sa.Column(
"modified",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.ForeignKeyConstraint(
["user_id"],
["users.id"],
name="fk_users_secrets_user_id_users",
onupdate="CASCADE",
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("user_id", name="users_secrets_pkey"),
)

# Copy password data from users table to users_secrets table
op.execute(
sa.DDL(
"""
INSERT INTO users_secrets (user_id, password_hash, modified)
SELECT id, password_hash, created_at
FROM users
WHERE password_hash IS NOT NULL
"""
)
)

op.drop_column("users", "password_hash")


def downgrade():
# Add column as nullable first
op.add_column(
"users",
sa.Column("password_hash", sa.VARCHAR(), autoincrement=False, nullable=True),
)

# Copy password data back from users_secrets table to users table
op.execute(
sa.DDL(
"""
UPDATE users
SET password_hash = us.password_hash
FROM users_secrets us
WHERE users.id = us.user_id
"""
)
)

# Now make the column NOT NULL
op.alter_column("users", "password_hash", nullable=False)

op.drop_table("users_secrets")
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,28 @@ class RefActions:
NO_ACTION: Final[str] = "NO ACTION"


def column_created_datetime(*, timezone: bool = True) -> sa.Column:
def column_created_datetime(
*, timezone: bool = True, doc="Timestamp auto-generated upon creation"
) -> sa.Column:
return sa.Column(
"created",
sa.DateTime(timezone=timezone),
nullable=False,
server_default=sa.sql.func.now(),
doc="Timestamp auto-generated upon creation",
doc=doc,
)


def column_modified_datetime(*, timezone: bool = True) -> sa.Column:
def column_modified_datetime(
*, timezone: bool = True, doc="Timestamp with last row update"
) -> sa.Column:
return sa.Column(
"modified",
sa.DateTime(timezone=timezone),
nullable=False,
server_default=sa.sql.func.now(),
onupdate=sa.sql.func.now(),
doc="Timestamp with last row update",
doc=doc,
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,6 @@
"NOTE: new policy (NK) is that the same phone can be reused therefore it does not has to be unique",
),
#
# User Secrets ------------------
#
sa.Column(
"password_hash",
sa.String(),
nullable=False,
doc="Hashed password",
),
#
# User Account ------------------
#
sa.Column(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import sqlalchemy as sa

from ._common import RefActions, column_modified_datetime
from .base import metadata

__all__: tuple[str, ...] = ("users_secrets",)

users_secrets = sa.Table(
"users_secrets",
metadata,
#
# User Secrets ------------------
#
sa.Column(
"user_id",
sa.BigInteger(),
sa.ForeignKey(
"users.id",
name="fk_users_secrets_user_id_users",
onupdate=RefActions.CASCADE,
ondelete=RefActions.CASCADE,
),
nullable=False,
),
sa.Column(
"password_hash",
sa.String(),
nullable=False,
doc="Hashed password",
),
column_modified_datetime(timezone=True, doc="Last password modification timestamp"),
# ---------------------------
sa.PrimaryKeyConstraint("user_id", name="users_secrets_pkey"),
)
Loading
Loading