Skip to content

Commit

Permalink
Add catalog and legacy organization
Browse files Browse the repository at this point in the history
  • Loading branch information
florimondmanca committed Jul 20, 2022
1 parent 2306635 commit b938c90
Show file tree
Hide file tree
Showing 24 changed files with 292 additions and 8 deletions.
3 changes: 3 additions & 0 deletions server/application/auth/commands.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from pydantic import EmailStr, SecretStr

from server.domain.common.types import ID
from server.domain.organizations.entities import LEGACY_ORGANIZATION_SIRET
from server.domain.organizations.types import Siret
from server.seedwork.application.commands import Command


class CreateUser(Command[ID]):
organization_siret: Siret = LEGACY_ORGANIZATION_SIRET
email: EmailStr
password: SecretStr

Expand Down
1 change: 1 addition & 0 deletions server/application/auth/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ async def create_user(

user = User(
id=id_,
organization_siret=command.organization_siret,
email=email,
password_hash=password_hash,
role=role,
Expand Down
3 changes: 3 additions & 0 deletions server/application/auth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@

from server.domain.auth.entities import UserRole
from server.domain.common.types import ID
from server.domain.organizations.types import Siret


class UserView(BaseModel):
id: ID
organization_siret: Siret
email: str
role: UserRole


class AuthenticatedUserView(BaseModel):
id: ID
organization_siret: Siret
email: str
role: UserRole
api_token: str
2 changes: 2 additions & 0 deletions server/application/catalog_records/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
from pydantic import BaseModel

from server.domain.common.types import ID
from server.domain.organizations.types import Siret


class CatalogRecordView(BaseModel):
id: ID
organization_siret: Siret
created_at: dt.datetime
3 changes: 3 additions & 0 deletions server/application/datasets/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@

from server.domain.common.types import ID
from server.domain.datasets.entities import DataFormat, UpdateFrequency
from server.domain.organizations.entities import LEGACY_ORGANIZATION_SIRET
from server.domain.organizations.types import Siret
from server.seedwork.application.commands import Command

from .validation import CreateDatasetValidationMixin, UpdateDatasetValidationMixin


class CreateDataset(CreateDatasetValidationMixin, Command[ID]):
organization_siret: Siret = LEGACY_ORGANIZATION_SIRET
title: str
description: str
service: str
Expand Down
5 changes: 4 additions & 1 deletion server/application/datasets/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ async def create_dataset(command: CreateDataset, *, id_: ID = None) -> ID:
id_ = repository.make_id()

catalog_record_id = await catalog_record_repository.insert(
CatalogRecord(id=catalog_record_repository.make_id())
CatalogRecord(
id=catalog_record_repository.make_id(),
organization_siret=command.organization_siret,
)
)
catalog_record = await catalog_record_repository.get_by_id(catalog_record_id)
assert catalog_record is not None
Expand Down
1 change: 1 addition & 0 deletions server/config/di.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ async def create_todo(...):
"server.infrastructure.tags.module.TagsModule",
"server.infrastructure.licenses.module.LicensesModule",
"server.infrastructure.organizations.module.OrganizationsModule",
"server.infrastructure.catalogs.module.CatalogsModule",
"server.infrastructure.auth.module.AuthModule",
]

Expand Down
2 changes: 2 additions & 0 deletions server/domain/auth/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from server.seedwork.domain.entities import Entity

from ..common.types import ID
from ..organizations.types import Siret


class UserRole(enum.Enum):
Expand All @@ -12,6 +13,7 @@ class UserRole(enum.Enum):

class User(Entity):
id: ID
organization_siret: Siret
email: str
password_hash: str
role: UserRole
Expand Down
2 changes: 2 additions & 0 deletions server/domain/catalog_records/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

from ..common import datetime as dtutil
from ..common.types import ID
from ..organizations.types import Siret


class CatalogRecord(Entity):
id: ID
organization_siret: Siret
created_at: dt.datetime = Field(default_factory=dtutil.now)
Empty file.
7 changes: 7 additions & 0 deletions server/domain/catalogs/entities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from server.seedwork.domain.entities import Entity

from ..organizations.types import Siret


class Catalog(Entity):
organization_siret: Siret
6 changes: 6 additions & 0 deletions server/domain/organizations/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@
class Organization(Entity):
name: str
siret: Siret


# A fake SIRET used for the organization that holds legacy users and whose catalog holds
# legacy datasets. "Legacy" means "before the multi-org system powered by DataPass.
# Going forward SIRET numbers will come from DataPass via the user's organization(s).
LEGACY_ORGANIZATION_SIRET = Siret("000 000 000 00000")
19 changes: 17 additions & 2 deletions server/infrastructure/auth/repositories.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
import uuid
from typing import Any, Optional
from typing import TYPE_CHECKING, Any, Optional

from sqlalchemy import Column, Enum, String, delete, select
from sqlalchemy import CHAR, Column, Enum, ForeignKey, String, delete, select
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.exc import NoResultFound
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import relationship

from server.application.auth.passwords import API_TOKEN_LENGTH
from server.domain.auth.entities import User, UserRole
from server.domain.auth.exceptions import UserDoesNotExist
from server.domain.auth.repositories import UserRepository
from server.domain.common.types import ID
from server.domain.organizations.types import Siret

from ..database import Base, Database

if TYPE_CHECKING:
from ..organizations.models import OrganizationModel


class UserModel(Base):
__tablename__ = "user"

id: uuid.UUID = Column(UUID(as_uuid=True), primary_key=True)
organization_siret: Siret = Column(
CHAR(14),
ForeignKey("organization.siret"),
nullable=False,
)
organization: "OrganizationModel" = relationship(
"OrganizationModel",
back_populates="users",
)
email = Column(String, nullable=False, unique=True, index=True)
password_hash = Column(String, nullable=False)
role = Column(Enum(UserRole, name="user_role_enum"), nullable=False)
Expand Down Expand Up @@ -69,6 +83,7 @@ async def insert(self, entity: User) -> ID:
async with self._db.session() as session:
instance = UserModel(
id=entity.id,
organization_siret=entity.organization_siret,
email=entity.email,
password_hash=entity.password_hash,
role=entity.role,
Expand Down
16 changes: 14 additions & 2 deletions server/infrastructure/catalog_records/repositories.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import datetime as dt
from typing import TYPE_CHECKING, Optional

from sqlalchemy import Column, DateTime, func, select
from sqlalchemy import CHAR, Column, DateTime, ForeignKey, func, select
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.exc import NoResultFound
from sqlalchemy.orm import relationship

from server.domain.catalog_records.entities import CatalogRecord
from server.domain.catalog_records.repositories import CatalogRecordRepository
from server.domain.common.types import ID
from server.domain.organizations.types import Siret

from ..database import Base, Database

if TYPE_CHECKING:
from ..catalogs.models import CatalogModel
from ..datasets.models import DatasetModel


Expand All @@ -23,17 +25,27 @@ class CatalogRecordModel(Base):
created_at: dt.datetime = Column(
DateTime(timezone=True), server_default=func.clock_timestamp(), nullable=False
)
organization_siret: Siret = Column(
CHAR(14),
ForeignKey("catalog.organization_siret"),
nullable=False,
)

catalog: "CatalogModel" = relationship(
"CatalogModel",
back_populates="catalog_records",
)

dataset: "DatasetModel" = relationship(
"DatasetModel",
back_populates="catalog_record",
cascade="delete",
)


def make_entity(instance: CatalogRecordModel) -> CatalogRecord:
return CatalogRecord(
id=instance.id,
organization_siret=instance.organization_siret,
created_at=instance.created_at,
)

Expand Down
Empty file.
37 changes: 37 additions & 0 deletions server/infrastructure/catalogs/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import datetime as dt
from typing import TYPE_CHECKING, List

from sqlalchemy import CHAR, Column, DateTime, ForeignKey, func
from sqlalchemy.orm import relationship

from server.domain.organizations.types import Siret

from ..database import Base

if TYPE_CHECKING:
from ..catalog_records.repositories import CatalogRecordModel
from ..organizations.models import OrganizationModel


class CatalogModel(Base):
__tablename__ = "catalog"

organization_siret: Siret = Column(
CHAR(14),
ForeignKey("organization.siret"),
primary_key=True,
)
organization: "OrganizationModel" = relationship(
"OrganizationModel",
back_populates="catalog",
)
created_at: dt.datetime = Column(
DateTime(timezone=True),
server_default=func.clock_timestamp(),
nullable=False,
)

catalog_records: List["CatalogRecordModel"] = relationship(
"CatalogRecordModel",
back_populates="catalog",
)
7 changes: 7 additions & 0 deletions server/infrastructure/catalogs/module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from server.seedwork.application.modules import Module

from . import models # noqa # Trigger SQLAlchemy table discovery.


class CatalogsModule(Module):
pass
18 changes: 18 additions & 0 deletions server/infrastructure/organizations/models.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
from typing import TYPE_CHECKING, List

from sqlalchemy import CHAR, Column, String
from sqlalchemy.orm import relationship

from server.domain.organizations.types import Siret

from ..database import Base

if TYPE_CHECKING:
from ..auth.repositories import UserModel
from ..catalogs.models import CatalogModel


class OrganizationModel(Base):
__tablename__ = "organization"

siret: Siret = Column(CHAR(14), primary_key=True)
name: str = Column(String(), nullable=False)

catalog: "CatalogModel" = relationship(
"CatalogModel",
back_populates="organization",
uselist=False,
)

users: List["UserModel"] = relationship(
"UserModel",
back_populates="organization",
)
34 changes: 34 additions & 0 deletions server/migrations/versions/4e40358ad25c_add_catalog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""add-catalog
Revision ID: 4e40358ad25c
Revises: da164fd0fa6f
Create Date: 2022-07-19 15:03:34.512545
"""
import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "4e40358ad25c"
down_revision = "da164fd0fa6f"
branch_labels = None
depends_on = None


def upgrade():
op.create_table(
"catalog",
sa.Column("organization_siret", sa.CHAR(length=14), nullable=False),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("clock_timestamp()"),
nullable=False,
),
sa.ForeignKeyConstraint(["organization_siret"], ["organization.siret"]),
sa.PrimaryKeyConstraint("organization_siret"),
)


def downgrade():
op.drop_table("catalog")
Loading

0 comments on commit b938c90

Please sign in to comment.