diff --git a/docker-compose.yml b/docker-compose.yml
index 596ae85..f234c89 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,7 +9,7 @@ services:
#- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
- POSTGRES_PASSWORD=kontor
healthcheck:
- test: ["CMD-SHELL", "pg_isready", "-U", "kontor"]
+ test: ["CMD-SHELL", "pg_isready -U kontor"]
interval: 1s
timeout: 5s
retries: 10
@@ -43,7 +43,7 @@ services:
volumes:
- mariadb-storage:/var/lib/mysql:rw
kontor:
- image: kontor:0.2.0-SNAPSHOT
+ image: kontor:0.1.0
restart: unless-stopped
networks:
- database
@@ -54,7 +54,7 @@ services:
postgres:
condition: service_healthy
kontor-api:
- image: kontor-api:0.2.0-SNAPSHOT
+ image: kontor-api:0.1.0
restart: unless-stopped
networks:
- database
diff --git a/kontor-api/.coverage b/kontor-api/.coverage
deleted file mode 100644
index 44d6ca2..0000000
Binary files a/kontor-api/.coverage and /dev/null differ
diff --git a/kontor-api/.gitignore b/kontor-api/.gitignore
index 4c49bd7..3373a70 100644
--- a/kontor-api/.gitignore
+++ b/kontor-api/.gitignore
@@ -1 +1,2 @@
.env
+.coverage
diff --git a/kontor-api/Makefile b/kontor-api/Makefile
index 2f20ba6..fa81df3 100644
--- a/kontor-api/Makefile
+++ b/kontor-api/Makefile
@@ -4,7 +4,7 @@ clean:
find . -name '*.py[co]' -delete
test:
- DB_HOST=localhost uv run pytest -v --cov --cov-report=term --cov-report=html:coverage-report
+ DB_SERVER=localhost uv run pytest -v --cov --cov-report=term --cov-report=html:coverage-report
docker: clean
docker build --target=production -t kontor-api:0.2.0-SNAPSHOT .
diff --git a/kontor-api/pyproject.toml b/kontor-api/pyproject.toml
index 19fae94..672f36b 100644
--- a/kontor-api/pyproject.toml
+++ b/kontor-api/pyproject.toml
@@ -22,5 +22,6 @@ dependencies = [
"python-jose>=3.4.0",
"python-multipart>=0.0.20",
"natsort>=8.4.0",
- "psycopg2>=2.9.10",
+ "psycopg2-binary>=2.9.10",
+ "pytest-cov>=6.1.1",
]
diff --git a/kontor-api/src/apis/base.py b/kontor-api/src/apis/base.py
index e969691..6c92e7f 100644
--- a/kontor-api/src/apis/base.py
+++ b/kontor-api/src/apis/base.py
@@ -3,6 +3,6 @@ from fastapi import APIRouter
from src.apis.version1 import comic, media, tysc
api_router = APIRouter(prefix="/api")
-api_router.include_router(comic.router, prefix="/comics", tags=["comics"])
-api_router.include_router(media.router, prefix="/media", tags=["media"])
-api_router.include_router(tysc.router, prefix="/tysc", tags=["tysc"])
+api_router.include_router(comic.router, tags=["comics"])
+api_router.include_router(media.router, tags=["media"])
+api_router.include_router(tysc.router, tags=["tysc"])
diff --git a/kontor-api/src/core/config.py b/kontor-api/src/core/config.py
index 6f8a4cf..a5e36c1 100644
--- a/kontor-api/src/core/config.py
+++ b/kontor-api/src/core/config.py
@@ -13,7 +13,7 @@ class Settings:
DB_USER: str = os.getenv("DB_USER", "kontor")
DB_PASSWORD: str = os.getenv("DB_PASSWORD", "kontor")
- DB_SERVER: str = os.getenv("DB_SERVER", "mariadb")
+ DB_SERVER: str = os.getenv("DB_SERVER", "postgres")
DB_PORT: str = os.getenv("DB_PORT", 5432)
DB_DBNAME: str = os.getenv("DB_DBNAME", "kontor")
DATABASE_URL: str = f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_SERVER}:{DB_PORT}/{DB_DBNAME}"
diff --git a/kontor-api/src/db/models/admin.py b/kontor-api/src/db/models/admin.py
index e16b0e0..caecff8 100644
--- a/kontor-api/src/db/models/admin.py
+++ b/kontor-api/src/db/models/admin.py
@@ -1,7 +1,6 @@
from datetime import datetime
-from sqlalchemy import Column, ForeignKey, Integer, String
-from sqlalchemy.dialects.mysql import BIT
+from sqlalchemy import Column, ForeignKey, Integer, String, Boolean
from sqlalchemy.orm import relationship, mapped_column, Mapped
from src.db.models.base import Base, BaseMixin
@@ -14,7 +13,7 @@ class Profile(Base, BaseMixin):
user_name = Column(String(255), nullable=False)
email = Column(String(255))
password = Column(String(255))
- enabled = Column(BIT(1))
+ enabled = Column(Boolean)
assignments = relationship("Assignment")
tokens = relationship("Token")
@@ -34,7 +33,7 @@ class Token(Base, BaseMixin):
token = Column(String(255), nullable=False, unique=True)
name = Column(String(255))
last_used_date: Mapped[datetime] = mapped_column()
- enabled = Column(BIT(1))
+ enabled = Column(Boolean)
profile_id = Column(String(255), ForeignKey("profile.id"), nullable=False)
profile = relationship("Profile", back_populates="tokens")
@@ -56,7 +55,7 @@ class Assignment(Base, BaseMixin):
class ModuleData(Base, BaseMixin):
__tablename__ = "module_data"
module_name = Column(String(255), nullable=False)
- import_data = Column(BIT(1))
+ import_data = Column(Boolean)
class MailAccount(Base, BaseMixin):
@@ -66,7 +65,7 @@ class MailAccount(Base, BaseMixin):
protocol = Column(String(255))
user_name = Column(String(255))
password = Column(String(255))
- start_tls = Column(BIT(1))
+ start_tls = Column(Boolean)
class Mail(Base, BaseMixin):
diff --git a/kontor-api/src/db/models/base.py b/kontor-api/src/db/models/base.py
index 4a354e7..5ef8183 100644
--- a/kontor-api/src/db/models/base.py
+++ b/kontor-api/src/db/models/base.py
@@ -1,8 +1,7 @@
import uuid
from datetime import datetime
-from sqlalchemy import func, Column, String
-from sqlalchemy.dialects.mysql import BIT
+from sqlalchemy import func, Column, String, Boolean
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
@@ -25,7 +24,7 @@ class BaseVideoMixin:
cloud_link = Column(String(255))
file_name = Column(String(255))
path = Column(String(255))
- review = Column(BIT(1))
+ review = Column(Boolean)
title = Column(String(255))
url = Column(String(255), unique=True)
- should_download = Column(BIT(1))
+ should_download = Column(Boolean)
diff --git a/kontor-api/src/db/models/comic.py b/kontor-api/src/db/models/comic.py
index 423ad7c..35962f3 100644
--- a/kontor-api/src/db/models/comic.py
+++ b/kontor-api/src/db/models/comic.py
@@ -1,7 +1,6 @@
from typing import Dict, List
from natsort import natsorted
-from sqlalchemy import Column, ForeignKey, Integer, String
-from sqlalchemy.dialects.mysql import BIT
+from sqlalchemy import Column, ForeignKey, Integer, String, Boolean
from sqlalchemy.orm import relationship
from src.db.models.base import Base, BaseMixin
@@ -24,8 +23,8 @@ class Comic(Base, BaseMixin):
title = Column(String(length=255), unique=True)
publisher_id = Column(String, ForeignKey('publisher.id'), nullable=False)
publisher = relationship("Publisher", back_populates="comics")
- current_order = Column(BIT(1))
- completed = Column(BIT(1))
+ current_order = Column(Boolean)
+ completed = Column(Boolean)
issues = relationship("Issue", order_by="Issue.issue_number")
story_arcs = relationship("StoryArc")
trade_paperbacks = relationship("TradePaperback")
@@ -81,8 +80,8 @@ class StoryArc(Base, BaseMixin):
class Issue(Base, BaseMixin):
__tablename__ = "issue"
issue_number = Column(String(255))
- in_stock = Column(BIT(1))
- is_read = Column(BIT(1))
+ in_stock = Column(Boolean)
+ is_read = Column(Boolean)
comic_id = Column(String, ForeignKey("comic.id"), nullable=False)
comic = relationship("Comic", back_populates="issues")
volume_id = Column(String, ForeignKey("volume.id"), nullable=True)
diff --git a/kontor-api/src/db/models/database.py b/kontor-api/src/db/models/database.py
index 0d5998b..9a69627 100644
--- a/kontor-api/src/db/models/database.py
+++ b/kontor-api/src/db/models/database.py
@@ -13,7 +13,7 @@ from sqlalchemy.orm import sessionmaker
from src.db.models.tysc import Card, CardSet, Rooster, Team, FieldPosition, Player, Vendor, Sport
from src.db.models.comic import Issue, TradePaperback, StoryArc, Volume, ComicWork, Artist, Comic, Publisher, WorkType
from src.db.models.bookshelf import ArticleAuthor, BookAuthor, BookshelfPublisher, Article, Book, Author
-from src.db.models.admin import Mail, MailAccount, ModuleData, Role, User, Token, AuthorizationMatrix
+from src.db.models.admin import Mail, MailAccount, ModuleData, Token, Assignment, Permission, Profile
from src.db.models.metadata import MetaDataTable, MetaDataColumn
from src.db.models.media import MediaVideo, MediaArticle, MediaFile, MediaActor, MediaActorFile
@@ -79,10 +79,10 @@ class KontorDB:
self.registry[MediaVideo.__tablename__] = MediaVideo
self.registry[MetaDataColumn.__tablename__] = MetaDataColumn
self.registry[MetaDataTable.__tablename__] = MetaDataTable
- self.registry[AuthorizationMatrix.__tablename__] = AuthorizationMatrix
+ self.registry[Assignment.__tablename__] = Assignment
self.registry[Token.__tablename__] = Token
- self.registry[User.__tablename__] = User
- self.registry[Role.__tablename__] = Role
+ self.registry[Profile.__tablename__] = Profile
+ self.registry[Permission.__tablename__] = Permission
self.registry[ModuleData.__tablename__] = ModuleData
self.registry[MailAccount.__tablename__] = MailAccount
self.registry[Mail.__tablename__] = Mail
diff --git a/kontor-api/src/db/models/media.py b/kontor-api/src/db/models/media.py
index 4c0b21a..2b19aa6 100644
--- a/kontor-api/src/db/models/media.py
+++ b/kontor-api/src/db/models/media.py
@@ -6,8 +6,7 @@ from pathlib import Path
import requests
from bs4 import BeautifulSoup
-from sqlalchemy import Column, String, ForeignKey
-from sqlalchemy.dialects.mysql import BIT
+from sqlalchemy import Column, String, ForeignKey, Boolean
from sqlalchemy.orm import relationship
from src.db.models.base import Base, BaseMixin, BaseVideoMixin
@@ -30,10 +29,10 @@ class MediaFile(Base, BaseMixin, BaseVideoMixin):
soup = BeautifulSoup(r.content, "html.parser")
title = soup.title.string
self.title = title
- self.review = 0
+ self.review = False
except:
self.title = None
- self.review = 1
+ self.review = True
self.last_modified_date = datetime.now()
def download_file(self, download_dir: str, dl_tool: str):
@@ -45,12 +44,12 @@ class MediaFile(Base, BaseMixin, BaseVideoMixin):
lines_list = output.splitlines()
file_name = self.__parse_output__(lines_list)
if file_name is None:
- self.review = 1
- self.should_download = 1
+ self.review = True
+ self.should_download = True
self.file_name = None
else:
download_file = Path(file_name)
- self.should_download = 0
+ self.should_download = False
self.file_name = download_file.name
self.cloud_link = str(download_file.absolute())
self.last_modified_date = datetime.now()
@@ -85,7 +84,7 @@ class MediaActorFile(Base, BaseMixin):
class MediaArticle(Base, BaseMixin):
__tablename__ = 'media_article'
- review = Column(BIT(1))
+ review = Column(Boolean)
title = Column(String(255))
url = Column(String(255), unique=True)
@@ -95,7 +94,7 @@ class MediaVideo(Base, BaseMixin):
cloud_link = Column(String(255))
file_name = Column(String(255))
path = Column(String(255))
- review = Column(BIT(1))
+ review = Column(Boolean)
title = Column(String(255))
url = Column(String(255), unique=True)
- should_download = Column(BIT(1))
+ should_download = Column(Boolean)
diff --git a/kontor-api/src/db/models/metadata.py b/kontor-api/src/db/models/metadata.py
index 081c3af..131f325 100644
--- a/kontor-api/src/db/models/metadata.py
+++ b/kontor-api/src/db/models/metadata.py
@@ -1,5 +1,4 @@
-from sqlalchemy import Column, String, ForeignKey, Integer
-from sqlalchemy.dialects.mysql import BIT
+from sqlalchemy import Column, String, ForeignKey, Integer, Boolean
from sqlalchemy.orm import relationship
from src.db.models.base import Base, BaseMixin
@@ -28,8 +27,8 @@ class MetaDataColumn(Base, BaseMixin):
table = relationship("MetaDataTable", back_populates="table_columns")
column_label = Column(String(255))
filter_label = Column(String(255))
- is_shown = Column(BIT(1))
- show_filter = Column(BIT(1))
+ is_shown = Column(Boolean)
+ show_filter = Column(Boolean)
ref_column = Column(String, nullable=True)
def __repr__(self):
diff --git a/kontor-api/src/db/models/tysc.py b/kontor-api/src/db/models/tysc.py
index dae0417..d3c391d 100644
--- a/kontor-api/src/db/models/tysc.py
+++ b/kontor-api/src/db/models/tysc.py
@@ -1,5 +1,4 @@
-from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint
-from sqlalchemy.dialects.mysql import BIT
+from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Boolean
from sqlalchemy.orm import relationship
from src.db.models.base import Base, BaseMixin
@@ -78,8 +77,8 @@ class CardSet(Base, BaseMixin):
UniqueConstraint("name", "vendor_id"),
)
name = Column(String(255), index=True)
- parallel_set = Column(BIT(1))
- insert_set = Column(BIT(1))
+ parallel_set = Column(Boolean)
+ insert_set = Column(Boolean)
vendor_id = Column(String, ForeignKey("vendor.id"), nullable=False, index=True)
vendor = relationship("Vendor", back_populates="card_sets")
cards = relationship("Card")
diff --git a/kontor-api/src/schema/comics/comic.py b/kontor-api/src/schema/comics/comic.py
index feec39d..1c7f548 100644
--- a/kontor-api/src/schema/comics/comic.py
+++ b/kontor-api/src/schema/comics/comic.py
@@ -46,8 +46,8 @@ def get_comic_details(comic: Comic) -> ComicDetailsResponse | None:
id=comic.id,
created=str(comic.created_date),
title=comic.title,
- completed=(comic.completed == 1),
- current_order=(comic.current_order == 1),
+ completed=comic.completed,
+ current_order=comic.current_order,
publisher=comic.publisher.name,
volumes=volumes,
works=works
diff --git a/kontor-api/src/schema/media/file.py b/kontor-api/src/schema/media/file.py
index 2c468e8..44f6b4f 100644
--- a/kontor-api/src/schema/media/file.py
+++ b/kontor-api/src/schema/media/file.py
@@ -23,8 +23,8 @@ def get_file_details(mediafile: MediaFile) -> MediaFileResponse | None:
file_name=mediafile.file_name,
cloud_link=mediafile.cloud_link,
url=str(mediafile.url),
- review=(mediafile.review == 1),
- should_download=(mediafile.should_download == 1))
+ review=mediafile.review,
+ should_download=mediafile.should_download)
#print(f"id: {mediafile.id}: review: {response.review} <- {mediafile.review}")
#print(f"id: {mediafile.id}: download: {response.should_download} <- {mediafile.should_download}")
return response
@@ -35,11 +35,5 @@ def set_file(model: MediaFileResponse, mediafile: MediaFile) -> None:
mediafile.url = model.url
mediafile.title = model.title
mediafile.last_modified_date = datetime.now()
- if model.review:
- mediafile.review = 1
- else:
- mediafile.review = 0
- if model.should_download:
- mediafile.should_download = 1
- else:
- mediafile.should_download = 0
+ mediafile.review = model.review
+ mediafile.should_download = model.should_download
diff --git a/kontor-api/src/templates/components/check.html b/kontor-api/src/templates/components/check.html
index b2b6263..41bf98c 100644
--- a/kontor-api/src/templates/components/check.html
+++ b/kontor-api/src/templates/components/check.html
@@ -1,4 +1,4 @@
-{% if check == 1 %}
+{% if check %}
{% else %}
diff --git a/kontor-api/tests/test_main.py b/kontor-api/tests/test_main.py
index 850d509..829f499 100644
--- a/kontor-api/tests/test_main.py
+++ b/kontor-api/tests/test_main.py
@@ -1,15 +1,15 @@
from fastapi.testclient import TestClient
import pytest
-from src.main import app
+from src.main import kontor
@pytest.fixture(name="client")
def client_fixture():
- client = TestClient(app)
+ client = TestClient(kontor)
yield client
def test_get_artists(client: TestClient):
- response = client.get("/comic/artists")
+ response = client.get("/api/comic/artists")
assert response.status_code == 200
assert len(response.json()) == 5
diff --git a/kontor-api/uv.lock b/kontor-api/uv.lock
index 1a9777f..2383d42 100644
--- a/kontor-api/uv.lock
+++ b/kontor-api/uv.lock
@@ -89,6 +89,35 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload_time = "2022-10-25T02:36:20.889Z" },
]
+[[package]]
+name = "coverage"
+version = "7.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/19/4f/2251e65033ed2ce1e68f00f91a0294e0f80c80ae8c3ebbe2f12828c4cd53/coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", size = 811872, upload_time = "2025-03-30T20:36:45.376Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f3/21/87e9b97b568e223f3438d93072479c2f36cc9b3f6b9f7094b9d50232acc0/coverage-7.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd", size = 211708, upload_time = "2025-03-30T20:35:47.417Z" },
+ { url = "https://files.pythonhosted.org/packages/75/be/882d08b28a0d19c9c4c2e8a1c6ebe1f79c9c839eb46d4fca3bd3b34562b9/coverage-7.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00", size = 211981, upload_time = "2025-03-30T20:35:49.002Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/1d/ce99612ebd58082fbe3f8c66f6d8d5694976c76a0d474503fa70633ec77f/coverage-7.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64", size = 245495, upload_time = "2025-03-30T20:35:51.073Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/8d/6115abe97df98db6b2bd76aae395fcc941d039a7acd25f741312ced9a78f/coverage-7.8.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067", size = 242538, upload_time = "2025-03-30T20:35:52.941Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/74/2f8cc196643b15bc096d60e073691dadb3dca48418f08bc78dd6e899383e/coverage-7.8.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008", size = 244561, upload_time = "2025-03-30T20:35:54.658Z" },
+ { url = "https://files.pythonhosted.org/packages/22/70/c10c77cd77970ac965734fe3419f2c98665f6e982744a9bfb0e749d298f4/coverage-7.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733", size = 244633, upload_time = "2025-03-30T20:35:56.221Z" },
+ { url = "https://files.pythonhosted.org/packages/38/5a/4f7569d946a07c952688debee18c2bb9ab24f88027e3d71fd25dbc2f9dca/coverage-7.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323", size = 242712, upload_time = "2025-03-30T20:35:57.801Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/a1/03a43b33f50475a632a91ea8c127f7e35e53786dbe6781c25f19fd5a65f8/coverage-7.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3", size = 244000, upload_time = "2025-03-30T20:35:59.378Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/89/ab6c43b1788a3128e4d1b7b54214548dcad75a621f9d277b14d16a80d8a1/coverage-7.8.0-cp313-cp313-win32.whl", hash = "sha256:dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d", size = 214195, upload_time = "2025-03-30T20:36:01.005Z" },
+ { url = "https://files.pythonhosted.org/packages/12/12/6bf5f9a8b063d116bac536a7fb594fc35cb04981654cccb4bbfea5dcdfa0/coverage-7.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487", size = 214998, upload_time = "2025-03-30T20:36:03.006Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/e6/1e9df74ef7a1c983a9c7443dac8aac37a46f1939ae3499424622e72a6f78/coverage-7.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25", size = 212541, upload_time = "2025-03-30T20:36:04.638Z" },
+ { url = "https://files.pythonhosted.org/packages/04/51/c32174edb7ee49744e2e81c4b1414ac9df3dacfcb5b5f273b7f285ad43f6/coverage-7.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42", size = 212767, upload_time = "2025-03-30T20:36:06.503Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/8f/f454cbdb5212f13f29d4a7983db69169f1937e869a5142bce983ded52162/coverage-7.8.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502", size = 256997, upload_time = "2025-03-30T20:36:08.137Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/74/2bf9e78b321216d6ee90a81e5c22f912fc428442c830c4077b4a071db66f/coverage-7.8.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1", size = 252708, upload_time = "2025-03-30T20:36:09.781Z" },
+ { url = "https://files.pythonhosted.org/packages/92/4d/50d7eb1e9a6062bee6e2f92e78b0998848a972e9afad349b6cdde6fa9e32/coverage-7.8.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4", size = 255046, upload_time = "2025-03-30T20:36:11.409Z" },
+ { url = "https://files.pythonhosted.org/packages/40/9e/71fb4e7402a07c4198ab44fc564d09d7d0ffca46a9fb7b0a7b929e7641bd/coverage-7.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73", size = 256139, upload_time = "2025-03-30T20:36:13.86Z" },
+ { url = "https://files.pythonhosted.org/packages/49/1a/78d37f7a42b5beff027e807c2843185961fdae7fe23aad5a4837c93f9d25/coverage-7.8.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a", size = 254307, upload_time = "2025-03-30T20:36:16.074Z" },
+ { url = "https://files.pythonhosted.org/packages/58/e9/8fb8e0ff6bef5e170ee19d59ca694f9001b2ec085dc99b4f65c128bb3f9a/coverage-7.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883", size = 255116, upload_time = "2025-03-30T20:36:18.033Z" },
+ { url = "https://files.pythonhosted.org/packages/56/b0/d968ecdbe6fe0a863de7169bbe9e8a476868959f3af24981f6a10d2b6924/coverage-7.8.0-cp313-cp313t-win32.whl", hash = "sha256:379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada", size = 214909, upload_time = "2025-03-30T20:36:19.644Z" },
+ { url = "https://files.pythonhosted.org/packages/87/e9/d6b7ef9fecf42dfb418d93544af47c940aa83056c49e6021a564aafbc91f/coverage-7.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257", size = 216068, upload_time = "2025-03-30T20:36:21.282Z" },
+ { url = "https://files.pythonhosted.org/packages/59/f1/4da7717f0063a222db253e7121bd6a56f6fb1ba439dcc36659088793347c/coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7", size = 203435, upload_time = "2025-03-30T20:36:43.61Z" },
+]
+
[[package]]
name = "dnspython"
version = "2.7.0"
@@ -287,8 +316,9 @@ dependencies = [
{ name = "natsort" },
{ name = "pathlib" },
{ name = "platformdirs" },
- { name = "psycopg2" },
+ { name = "psycopg2-binary" },
{ name = "pytest" },
+ { name = "pytest-cov" },
{ name = "python-dotenv" },
{ name = "python-jose" },
{ name = "python-multipart" },
@@ -307,8 +337,9 @@ requires-dist = [
{ name = "natsort", specifier = ">=8.4.0" },
{ name = "pathlib", specifier = ">=1.0.1" },
{ name = "platformdirs", specifier = ">=4.3.7" },
- { name = "psycopg2", specifier = ">=2.9.10" },
+ { name = "psycopg2-binary", specifier = ">=2.9.10" },
{ name = "pytest", specifier = "==7.4.0" },
+ { name = "pytest-cov", specifier = ">=6.1.1" },
{ name = "python-dotenv", specifier = ">=1.1.0" },
{ name = "python-jose", specifier = ">=3.4.0" },
{ name = "python-multipart", specifier = ">=0.0.20" },
@@ -426,12 +457,22 @@ wheels = [
]
[[package]]
-name = "psycopg2"
+name = "psycopg2-binary"
version = "2.9.10"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/62/51/2007ea29e605957a17ac6357115d0c1a1b60c8c984951c19419b3474cdfd/psycopg2-2.9.10.tar.gz", hash = "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11", size = 385672, upload_time = "2024-10-16T11:24:54.832Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764, upload_time = "2024-10-16T11:24:58.126Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ae/49/a6cfc94a9c483b1fa401fbcb23aca7892f60c7269c5ffa2ac408364f80dc/psycopg2-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:91fd603a2155da8d0cfcdbf8ab24a2d54bca72795b90d2a3ed2b6da8d979dee2", size = 2569060, upload_time = "2025-01-04T20:09:15.28Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699, upload_time = "2024-10-16T11:21:42.841Z" },
+ { url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245, upload_time = "2024-10-16T11:21:51.989Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631, upload_time = "2024-10-16T11:21:57.584Z" },
+ { url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140, upload_time = "2024-10-16T11:22:02.005Z" },
+ { url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762, upload_time = "2024-10-16T11:22:06.412Z" },
+ { url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967, upload_time = "2024-10-16T11:22:11.583Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326, upload_time = "2024-10-16T11:22:16.406Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712, upload_time = "2024-10-16T11:22:21.366Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155, upload_time = "2024-10-16T11:22:25.684Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356, upload_time = "2024-10-16T11:22:30.562Z" },
+ { url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224, upload_time = "2025-01-04T20:09:19.234Z" },
]
[[package]]
@@ -510,6 +551,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/33/b2/741130cbcf2bbfa852ed95a60dc311c9e232c7ed25bac3d9b8880a8df4ae/pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32", size = 323580, upload_time = "2023-06-23T11:17:25.738Z" },
]
+[[package]]
+name = "pytest-cov"
+version = "6.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "coverage" },
+ { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload_time = "2025-04-05T14:07:51.592Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload_time = "2025-04-05T14:07:49.641Z" },
+]
+
[[package]]
name = "python-dotenv"
version = "1.1.0"
diff --git a/kontor-scripts/config.py b/kontor-scripts/config.py
index bf65cd5..6e06702 100644
--- a/kontor-scripts/config.py
+++ b/kontor-scripts/config.py
@@ -3,6 +3,7 @@ Setup database connections
"""
import sqlite3
import mariadb
+import psycopg2
import logging.config
from platformdirs import PlatformDirs
from pathlib import Path
@@ -24,7 +25,8 @@ def get_database_cursors(log, config: str):
password=db_config['mariadb']['password'],
database=db_config['mariadb']['database']
)
- return sqlite_conn, mariadb_conn
+ postgres_conn = psycopg2.connect(f"host={db_config['postgres']['host']} port={db_config['postgres']['port']} user={db_config['postgres']['user']} password={db_config['postgres']['password']} dbname={db_config['postgres']['']}")
+ return sqlite_conn, mariadb_conn, postgres_conn
def create_tables(sqlite_conn, logger, recreate_db, scripts):
diff --git a/kontor-scripts/json_to_postgres.py b/kontor-scripts/json_to_postgres.py
new file mode 100644
index 0000000..e5b1240
--- /dev/null
+++ b/kontor-scripts/json_to_postgres.py
@@ -0,0 +1,59 @@
+"""
+copy data from JSON to Postgres
+"""
+from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
+from pathlib import Path
+from config import get_logger, get_database_cursors
+import json
+import psycopg2
+from psycopg2.sql import SQL
+
+parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
+parser.add_argument('--verbose', '-v', action='count', default=0)
+parser.add_argument('--config', '-c', default='kontor-docker')
+parser.add_argument('--file', '-f', default='~/.sync/media/data.json')
+args = parser.parse_args()
+
+def copy_data(postgres_conn, data_file: Path, log):
+ postgres_cursor = postgres_conn.cursor()
+ import_file = Path(data_file)
+ if not import_file.exists():
+ log.info(f"File {data_file} does not exist. Do nothing.")
+ return
+ log.info("read json file")
+ with open(data_file, 'r') as json_file:
+ json_load = json.load(json_file)
+ for table in json_load:
+ log.info(f"{table}: {len(json_load[table])}")
+ # result[table] = import_table(table, json_load[table])
+ truncate_statement = 'TRUNCATE {}'.format(table)
+ #log.info(f"truncate: {truncate_statement}")
+ postgres_cursor.execute("SET FOREIGN_KEY_CHECKS = 0")
+ postgres_cursor.execute(truncate_statement)
+ items = json_load[table]
+ for item in items:
+ #log.info(f"item: {item}")
+ values = []
+ columns = []
+ for (key, value) in item.items():
+ columns.append(key)
+ values.append(value)
+ row = tuple(values)
+ log.info(f"values: {row}")
+ insert_statement = 'INSERT INTO {}({}) VALUES({})'.format(table, ', '.join(columns), ', '.join(['%s']*len(columns)))
+ #log.info(f"statement: {insert_statement}")
+ postgres_cursor.execute(SQL(insert_statement), row)
+ try:
+ postgres_conn.commit()
+ except psycopg2.Error as error:
+ log.info('insert failed with %s', error)
+
+
+if __name__ == '__main__':
+ logger = get_logger(args.verbose, args.config)
+ logger.info('kontor.json_to_postgres started')
+ _, _, p_conn = get_database_cursors(logger, args.config)
+ copy_data(p_conn, args.file, logger)
+ p_conn.close()
+ logger.info('kontor.json_to_postgres finished')
+
diff --git a/kontor-spring/Dockerfile b/kontor-spring/Dockerfile
index d4f3463..1cfd701 100644
--- a/kontor-spring/Dockerfile
+++ b/kontor-spring/Dockerfile
@@ -1,5 +1,5 @@
FROM alpine/java:21-jdk
WORKDIR /
-ADD build/libs/kontor-spring-0.1.0-SNAPSHOT.jar app.jar
+ADD build/libs/kontor-spring-0.2.0-SNAPSHOT.jar app.jar
EXPOSE 8000
CMD ["java", "-jar", "-Dspring.profiles.active=prod", "-Dvaadin.productionMode=true", "app.jar"]
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/AuthorizationMatrix.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/AuthorizationMatrix.java
deleted file mode 100644
index 26b5ebb..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/AuthorizationMatrix.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package de.thpeetz.kontor.admin.data;
-
-import de.thpeetz.kontor.common.data.AbstractEntity;
-import jakarta.persistence.Entity;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
-import jakarta.validation.constraints.NotNull;
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-
-@Getter
-@Setter
-@Entity
-public class AuthorizationMatrix extends AbstractEntity {
-
- @ManyToOne
- @JoinColumn(name = "user_id")
- @NotNull
- private User user;
-
- @ManyToOne
- @JoinColumn(name = "role_id")
- @NotNull
- private Role role;
-
- @Override
- public String toString() {
- final StringBuffer sb = new StringBuffer("AuthorizationMatrix{");
- sb.append("user=").append(user.getUserName());
- sb.append(", role=").append(role.getName());
- sb.append('}');
- return sb.toString();
- }
-}
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/Role.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/Role.java
deleted file mode 100644
index 5ad81c2..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/Role.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package de.thpeetz.kontor.admin.data;
-
-import java.util.List;
-
-import de.thpeetz.kontor.common.data.AbstractEntity;
-import jakarta.persistence.Entity;
-import jakarta.persistence.FetchType;
-import jakarta.persistence.OneToMany;
-import jakarta.validation.constraints.NotEmpty;
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-import lombok.extern.slf4j.Slf4j;
-
-import javax.annotation.Nullable;
-
-@Slf4j
-@Getter
-@Setter
-@ToString
-@Entity
-public class Role extends AbstractEntity {
-
- @NotEmpty
- private String name;
-
- @OneToMany(fetch = FetchType.EAGER, mappedBy = "role")
- @Nullable
- private List matrix;
-}
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/User.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/User.java
deleted file mode 100644
index ec16233..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/data/User.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package de.thpeetz.kontor.admin.data;
-
-import de.thpeetz.kontor.common.data.AbstractEntity;
-import jakarta.persistence.Entity;
-import jakarta.persistence.FetchType;
-import jakarta.persistence.Index;
-import jakarta.persistence.OneToMany;
-import jakarta.persistence.Table;
-import jakarta.persistence.UniqueConstraint;
-import jakarta.validation.constraints.NotEmpty;
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-import lombok.extern.slf4j.Slf4j;
-
-import javax.annotation.Nullable;
-import java.util.LinkedList;
-import java.util.List;
-
-@Slf4j
-@Getter
-@Setter
-@ToString
-@Entity
-@Table(indexes = @Index(columnList = "userName"), uniqueConstraints = @UniqueConstraint(columnNames = {"userName"}))
-public class User extends AbstractEntity {
-
- private String firstName;
-
- private String lastName;
-
- @NotEmpty
- private String userName;
-
- private String email;
-
- private String password;
-
- private boolean enabled;
-
- @OneToMany(fetch = FetchType.EAGER, mappedBy = "user")
- @Nullable
- private List matrix = new LinkedList<>();
-
- @OneToMany(fetch = FetchType.EAGER, mappedBy = "user")
- @Nullable
- private List tokens = new LinkedList<>();
-
- public String getFullName() {
- StringBuilder fullNamBuilder = new StringBuilder();
- if (firstName != null) {
- fullNamBuilder.append(firstName);
- }
- if (lastName != null) {
- if (fullNamBuilder.length() > 0) {
- fullNamBuilder.append(" ");
- }
- fullNamBuilder.append(lastName);
- }
- return fullNamBuilder.toString();
- }
-}
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/AuthorizationMatrixRepository.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/AuthorizationMatrixRepository.java
deleted file mode 100644
index 3e5c30c..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/AuthorizationMatrixRepository.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package de.thpeetz.kontor.admin.repository;
-
-import java.util.List;
-
-import de.thpeetz.kontor.admin.data.AuthorizationMatrix;
-import de.thpeetz.kontor.admin.data.Role;
-import de.thpeetz.kontor.admin.data.User;
-import org.springframework.data.jpa.repository.JpaRepository;
-
-public interface AuthorizationMatrixRepository extends JpaRepository {
-
- List findByUser(User user);
-
- List findByRole(Role role);
-}
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/RoleRepository.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/RoleRepository.java
deleted file mode 100644
index 0d1b466..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/RoleRepository.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package de.thpeetz.kontor.admin.repository;
-
-import java.util.List;
-
-import de.thpeetz.kontor.admin.data.Role;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
-
-public interface RoleRepository extends JpaRepository {
-
- @Query("select r from Role r " +
- "where lower(r.name) like lower(concat('%', :searchTerm, '%')) ")
- List search(@Param("searchTerm") String searchTerm);
-
- @Query("select r from Role r " +
- "where lower(r.name) like lower(:name) ")
- Role findByName(@Param("name") String name);
-}
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/UserRepository.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/UserRepository.java
deleted file mode 100644
index c8c20f3..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/repository/UserRepository.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package de.thpeetz.kontor.admin.repository;
-
-import java.util.List;
-
-import de.thpeetz.kontor.admin.data.User;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
-
-public interface UserRepository extends JpaRepository {
-
- @Query("select u from User u " +
- "where lower(u.lastName) like lower(concat('%', :searchTerm, '%')) ")
- List search(@Param("searchTerm") String searchTerm);
-
- User findByUserName(String userName);
-}
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/services/KontorUserDetailsService.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/services/KontorUserDetailsService.java
index 52fc79a..21f76ac 100644
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/services/KontorUserDetailsService.java
+++ b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/services/KontorUserDetailsService.java
@@ -1,7 +1,11 @@
package de.thpeetz.kontor.admin.services;
-import de.thpeetz.kontor.admin.data.*;
-import de.thpeetz.kontor.admin.repository.*;
+import de.thpeetz.kontor.admin.data.Assignment;
+import de.thpeetz.kontor.admin.data.Profile;
+import de.thpeetz.kontor.admin.data.Permission;
+import de.thpeetz.kontor.admin.repository.AssignmentRepository;
+import de.thpeetz.kontor.admin.repository.ProfileRepository;
+import de.thpeetz.kontor.admin.repository.PermissionRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
@@ -21,7 +25,7 @@ import java.util.stream.Collectors;
@Service("userDetailsService")
public class KontorUserDetailsService implements UserDetailsService {
- private static SecureRandom random = new SecureRandom();
+ private static final SecureRandom random = new SecureRandom();
@Autowired
private ProfileRepository profileRepository;
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationForm.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationForm.java
deleted file mode 100644
index da593f8..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationForm.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package de.thpeetz.kontor.admin.views;
-
-import java.util.List;
-
-import com.vaadin.flow.component.ComponentEvent;
-import com.vaadin.flow.component.ComponentEventListener;
-import com.vaadin.flow.component.Key;
-import com.vaadin.flow.component.button.Button;
-import com.vaadin.flow.component.button.ButtonVariant;
-import com.vaadin.flow.component.combobox.ComboBox;
-import com.vaadin.flow.component.formlayout.FormLayout;
-import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
-import com.vaadin.flow.data.binder.BeanValidationBinder;
-import com.vaadin.flow.data.binder.Binder;
-
-import de.thpeetz.kontor.admin.data.AuthorizationMatrix;
-import de.thpeetz.kontor.admin.data.Role;
-import de.thpeetz.kontor.admin.data.User;
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-public class AuthorizationForm extends FormLayout {
-
- ComboBox user = new ComboBox<>("User");
- ComboBox role = new ComboBox<>("Role");
-
- Button save = new Button("Save");
- Button delete = new Button("Delete");
- Button close = new Button("Cancel");
-
- Binder binder = new BeanValidationBinder<>(AuthorizationMatrix.class);
-
- public AuthorizationForm(List users, List roles) {
- addClassName("authorizationmatrix-form");
- binder.bindInstanceFields(this);
-
- user.setItems(users);
- user.setItemLabelGenerator(User::getUserName);
- role.setItems(roles);
- role.setItemLabelGenerator(Role::getName);
- add(user, role, createButtonsLayout());
- }
-
- private HorizontalLayout createButtonsLayout() {
- save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
- delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
- close.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
-
- save.addClickShortcut(Key.ENTER);
- close.addClickShortcut(Key.ESCAPE);
-
- save.addClickListener(event -> validateAndSave());
- delete.addClickListener(event -> fireEvent(new DeleteEvent(this, binder.getBean())));
- close.addClickListener(event -> fireEvent(new CloseEvent(this)));
-
- binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));
- return new HorizontalLayout(save, delete, close);
- }
-
- private void validateAndSave() {
- if (binder.isValid()) {
- fireEvent(new SaveEvent(this, binder.getBean()));
- }
- }
-
- public void setAuthorizationMatrix(AuthorizationMatrix authorizationMatrix) {
- binder.setBean(authorizationMatrix);
- }
-
- public abstract static class AuthorizationFormEvent extends ComponentEvent {
- private AuthorizationMatrix authorizationMatrix;
-
- protected AuthorizationFormEvent(AuthorizationForm source, AuthorizationMatrix authorizationMatrix) {
- super(source, false);
- this.authorizationMatrix = authorizationMatrix;
- }
-
- public AuthorizationMatrix getAuthorizationMatrix() {
- return authorizationMatrix;
- }
- }
-
- public static class SaveEvent extends AuthorizationFormEvent {
- SaveEvent(AuthorizationForm source, AuthorizationMatrix authorizationMatrix) {
- super(source, authorizationMatrix);
- }
- }
-
- public static class DeleteEvent extends AuthorizationFormEvent {
- DeleteEvent(AuthorizationForm source, AuthorizationMatrix authorizationMatrix) {
- super(source, authorizationMatrix);
- }
- }
-
- public static class CloseEvent extends AuthorizationFormEvent {
- CloseEvent(AuthorizationForm source) {
- super(source, null);
- }
- }
-
- public void addDeleteListener(ComponentEventListener listener) {
- addListener(DeleteEvent.class, listener);
- }
-
- public void addSaveListener(ComponentEventListener listener) {
- addListener(SaveEvent.class, listener);
- }
-
- public void addCloseListener(ComponentEventListener listener) {
- addListener(CloseEvent.class, listener);
- }
-}
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationView.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationView.java
deleted file mode 100644
index 435ae8f..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/AuthorizationView.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package de.thpeetz.kontor.admin.views;
-
-import org.springframework.context.annotation.Scope;
-
-import com.vaadin.flow.component.Component;
-import com.vaadin.flow.component.button.Button;
-import com.vaadin.flow.component.grid.Grid;
-import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
-import com.vaadin.flow.component.orderedlayout.VerticalLayout;
-
-import com.vaadin.flow.router.PageTitle;
-import com.vaadin.flow.router.Route;
-import com.vaadin.flow.spring.annotation.SpringComponent;
-
-import de.thpeetz.kontor.admin.AdminConstants;
-import de.thpeetz.kontor.admin.data.AuthorizationMatrix;
-import de.thpeetz.kontor.admin.services.AdminService;
-import de.thpeetz.kontor.common.views.MainLayout;
-import jakarta.annotation.security.RolesAllowed;
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-@SpringComponent
-@Scope("prototype")
-@RolesAllowed("ROLE_ADMIN")
-@Route(value = AdminConstants.AUTHORIZATION_ROUTE, layout = MainLayout.class)
-@PageTitle("Authorization | Admin | Kontor")
-public class AuthorizationView extends VerticalLayout {
-
- Grid grid = new Grid<>(AuthorizationMatrix.class);
- AuthorizationForm form;
- AdminService service;
-
- public AuthorizationView(AdminService service) {
- this.service = service;
- addClassName("authoriaztionmatrix-view");
- setSizeFull();
- configureGrid();
- configureForm();
-
- add(getToolbar(), getContent());
- updateList();
- }
-
- private void configureGrid() {
- grid.addClassName("authorizationmatrix-grid");
- grid.setSizeFull();
- grid.setColumns("user.userName", "role.name");
- grid.getColumns().forEach(col -> col.setAutoWidth(true));
- grid.asSingleSelect().addValueChangeListener(event -> editAuthorizationMatrix(event.getValue()));
- }
-
- private void configureForm() {
- form = new AuthorizationForm(service.findAllUsers(), service.findAllRoles());
- form.setWidth("25em");
- form.addSaveListener(this::saveAuthorizationMatrix);
- form.addDeleteListener(this::deleteAuthorizationMatrix);
- form.addCloseListener(e -> closeEditor());
- }
-
- private void saveAuthorizationMatrix(AuthorizationForm.SaveEvent event) {
- AuthorizationMatrix authorizationMatrix = event.getAuthorizationMatrix();
- service.saveAuthorizationMatrix(authorizationMatrix);
- updateList();
- closeEditor();
- }
-
- private void deleteAuthorizationMatrix(AuthorizationForm.DeleteEvent event) {
- service.deleteAuthorizationMatrix(event.getAuthorizationMatrix());
- updateList();
- closeEditor();
- }
-
- private Component getContent() {
- HorizontalLayout content = new HorizontalLayout(grid, form);
- content.setFlexGrow(2, grid);
- content.setFlexGrow(1, form);
- content.addClassName("content");
- content.setSizeFull();
- return content;
- }
-
- private HorizontalLayout getToolbar() {
- Button addAuthorizationMaxtrixButton = new Button("Add permssion", click -> addAuthorizationMatrix());
- HorizontalLayout toolbar = new HorizontalLayout(addAuthorizationMaxtrixButton);
- toolbar.addClassName("toolbar");
- return toolbar;
- }
-
- public void editAuthorizationMatrix(AuthorizationMatrix authorizationMatrix) {
- if (authorizationMatrix == null) {
- closeEditor();
- } else {
- form.setAuthorizationMatrix(authorizationMatrix);
- form.setVisible(true);
- addClassName("editing");
- }
- }
-
- public void closeEditor() {
- form.setAuthorizationMatrix(null);
- form.setVisible(false);
- removeClassName("editing");
- }
-
- private void addAuthorizationMatrix() {
- grid.asSingleSelect().clear();
- editAuthorizationMatrix(new AuthorizationMatrix());
- }
-
- private void updateList() {
- grid.setItems(service.findAllAuthorizationMatrices());
- }
-}
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleForm.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleForm.java
deleted file mode 100644
index 671d0f5..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleForm.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package de.thpeetz.kontor.admin.views;
-
-import com.vaadin.flow.component.ComponentEvent;
-import com.vaadin.flow.component.ComponentEventListener;
-import com.vaadin.flow.component.Key;
-import com.vaadin.flow.component.button.Button;
-import com.vaadin.flow.component.button.ButtonVariant;
-import com.vaadin.flow.component.formlayout.FormLayout;
-import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
-import com.vaadin.flow.component.textfield.TextField;
-import com.vaadin.flow.data.binder.BeanValidationBinder;
-import com.vaadin.flow.data.binder.Binder;
-
-import de.thpeetz.kontor.admin.data.Role;
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-public class RoleForm extends FormLayout {
-
- TextField name = new TextField("Role name");
-
- Button save = new Button("Save");
- Button delete = new Button("Delete");
- Button close = new Button("Cancel");
-
- Binder binder = new BeanValidationBinder<>(Role.class);
-
- public RoleForm() {
- addClassName("role-form");
- binder.bindInstanceFields(this);
- add(name, createButtonsLayout());
- }
-
- private HorizontalLayout createButtonsLayout() {
- save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
- delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
- close.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
-
- save.addClickShortcut(Key.ENTER);
- close.addClickShortcut(Key.ESCAPE);
-
- save.addClickListener(event -> validateAndSave());
- delete.addClickListener(event -> fireEvent(new DeleteEvent(this, binder.getBean())));
- close.addClickListener(event -> fireEvent(new CloseEvent(this)));
-
- binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));
- return new HorizontalLayout(save, delete, close);
- }
-
- private void validateAndSave() {
- if (binder.isValid()) {
- fireEvent(new SaveEvent(this, binder.getBean()));
- }
- }
-
- public void setRole(Role role) {
- binder.setBean(role);
- }
-
- public abstract static class RoleFormEvent extends ComponentEvent {
- private Role role;
-
- protected RoleFormEvent(RoleForm source, Role role) {
- super(source, false);
- this.role = role;
- }
-
- public Role getRole() {
- return role;
- }
- }
-
- public static class SaveEvent extends RoleFormEvent {
- SaveEvent(RoleForm source, Role role) {
- super(source, role);
- }
- }
-
- public static class DeleteEvent extends RoleFormEvent {
- DeleteEvent(RoleForm source, Role role) {
- super(source, role);
- }
- }
-
- public static class CloseEvent extends RoleFormEvent {
- CloseEvent(RoleForm source) {
- super(source, null);
- }
- }
-
- public void addDeleteListener(ComponentEventListener listener) {
- addListener(DeleteEvent.class, listener);
- }
-
- public void addSaveListener(ComponentEventListener listener) {
- addListener(SaveEvent.class, listener);
- }
-
- public void addCloseListener(ComponentEventListener listener) {
- addListener(CloseEvent.class, listener);
- }
-}
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleView.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleView.java
deleted file mode 100644
index 1f13c9a..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/RoleView.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package de.thpeetz.kontor.admin.views;
-
-import org.springframework.context.annotation.Scope;
-
-import com.vaadin.flow.component.Component;
-import com.vaadin.flow.component.button.Button;
-import com.vaadin.flow.component.grid.Grid;
-import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
-import com.vaadin.flow.component.orderedlayout.VerticalLayout;
-import com.vaadin.flow.component.textfield.TextField;
-import com.vaadin.flow.data.value.ValueChangeMode;
-import com.vaadin.flow.router.PageTitle;
-import com.vaadin.flow.router.Route;
-import com.vaadin.flow.spring.annotation.SpringComponent;
-
-import de.thpeetz.kontor.admin.AdminConstants;
-import de.thpeetz.kontor.admin.data.Role;
-import de.thpeetz.kontor.admin.services.AdminService;
-import de.thpeetz.kontor.common.views.MainLayout;
-import jakarta.annotation.security.RolesAllowed;
-import lombok.extern.slf4j.Slf4j;
-
-@Slf4j
-@SpringComponent
-@Scope("prototype")
-@RolesAllowed("ROLE_ADMIN")
-@Route(value = AdminConstants.ROLE_ROUTE, layout = MainLayout.class)
-@PageTitle("Rollen | Admin | Kontor")
-public class RoleView extends VerticalLayout {
- Grid grid = new Grid<>(Role.class);
- TextField filterText = new TextField();
- RoleForm form;
- AdminService service;
-
- public RoleView(AdminService service) {
- this.service = service;
- addClassName("user-view");
- setSizeFull();
- configureGrid();
- configureForm();
-
- add(getToolbar(), getContent());
- updateList();
- }
-
- private void configureGrid() {
- grid.addClassName("user-grid");
- grid.setSizeFull();
- grid.setColumns("name");
- grid.getColumns().forEach(col -> col.setAutoWidth(true));
- grid.asSingleSelect().addValueChangeListener(event -> editRole(event.getValue()));
- }
-
- private void configureForm() {
- form = new RoleForm();
- form.setWidth("25em");
- form.addSaveListener(this::saveRole);
- form.addDeleteListener(this::deleteRole);
- form.addCloseListener(e -> closeEditor());
- }
-
- private void saveRole(RoleForm.SaveEvent event) {
- service.saveRole(event.getRole());
- updateList();
- closeEditor();
- }
-
- private void deleteRole(RoleForm.DeleteEvent event) {
- service.deleteRole(event.getRole());
- updateList();
- closeEditor();
- }
-
- private Component getContent() {
- HorizontalLayout content = new HorizontalLayout(grid, form);
- content.setFlexGrow(2, grid);
- content.setFlexGrow(1, form);
- content.addClassName("content");
- content.setSizeFull();
- return content;
- }
-
- private HorizontalLayout getToolbar() {
- filterText.setPlaceholder("Filter by user name...");
- filterText.setClearButtonVisible(true);
- filterText.setValueChangeMode(ValueChangeMode.LAZY);
- filterText.addValueChangeListener(e -> updateList());
- Button addUserButton = new Button("Add user", click -> addUser());
- HorizontalLayout toolbar = new HorizontalLayout(filterText, addUserButton);
- toolbar.addClassName("toolbar");
- return toolbar;
- }
-
- public void editRole(Role role) {
- if (role == null) {
- closeEditor();
- } else {
- form.setRole(role);
- form.setVisible(true);
- addClassName("editing");
- }
- }
-
- public void closeEditor() {
- form.setRole(null);
- form.setVisible(false);
- removeClassName("editing");
- }
-
- private void addUser() {
- grid.asSingleSelect().clear();
- editRole(new Role());
- }
-
- private void updateList() {
- grid.setItems(service.findAllRoles(filterText.getValue()));
- }
-}
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserForm.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserForm.java
deleted file mode 100644
index 21298f3..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserForm.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package de.thpeetz.kontor.admin.views;
-
-import com.vaadin.flow.component.ComponentEvent;
-import com.vaadin.flow.component.ComponentEventListener;
-import com.vaadin.flow.component.Key;
-import com.vaadin.flow.component.button.Button;
-import com.vaadin.flow.component.button.ButtonVariant;
-import com.vaadin.flow.component.checkbox.Checkbox;
-import com.vaadin.flow.component.checkbox.CheckboxGroup;
-import com.vaadin.flow.component.checkbox.CheckboxGroupVariant;
-import com.vaadin.flow.component.formlayout.FormLayout;
-import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
-import com.vaadin.flow.component.textfield.EmailField;
-import com.vaadin.flow.component.textfield.PasswordField;
-import com.vaadin.flow.component.textfield.TextField;
-import com.vaadin.flow.data.binder.BeanValidationBinder;
-import com.vaadin.flow.data.binder.Binder;
-import de.thpeetz.kontor.admin.data.Role;
-import de.thpeetz.kontor.admin.data.User;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.List;
-
-@Slf4j
-public class UserForm extends FormLayout {
-
- TextField userName = new TextField("User name");
- PasswordField password = new PasswordField("Password");
- EmailField email = new EmailField("Email");
- TextField firstName = new TextField("First name");
- TextField lastName = new TextField("Last name");
- Checkbox enabled = new Checkbox("Enabled");
- String originalPassword;
-
- CheckboxGroup permissions = new CheckboxGroup<>("Permissions");
-
- Button save = new Button("Save");
- Button delete = new Button("Delete");
- Button close = new Button("Cancel");
-
- Binder binder = new BeanValidationBinder<>(User.class);
-
- public UserForm() {
- addClassName("user-form");
- binder.bindInstanceFields(this);
- add(userName, password, email, firstName, lastName, enabled, configurePermissionsGroup(), createButtonsLayout());
- }
-
- private CheckboxGroup configurePermissionsGroup() {
- permissions.addThemeVariants(CheckboxGroupVariant.LUMO_VERTICAL);
- permissions.setItemLabelGenerator(Role::getName);
- permissions.addValueChangeListener(event -> {
- log.debug("permissions changed: {}", event);
- });
- return permissions;
- }
-
- private HorizontalLayout createButtonsLayout() {
- save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
- delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
- close.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
-
- save.addClickShortcut(Key.ENTER);
- close.addClickShortcut(Key.ESCAPE);
-
- save.addClickListener(event -> validateAndSave());
- delete.addClickListener(event -> fireEvent(new DeleteEvent(this, binder.getBean())));
- close.addClickListener(event -> fireEvent(new CloseEvent(this)));
-
- binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));
- return new HorizontalLayout(save, delete, close);
- }
-
- private void validateAndSave() {
- if (binder.isValid()) {
- fireEvent(new SaveEvent(this, binder.getBean()));
- }
- }
-
- public void setUser(User user) {
- binder.setBean(user);
- //log.debug("UserForm.setUser: {}", user);
- if (user != null) {
- this.originalPassword = user.getPassword();
- } else {
- this.originalPassword = null;
- }
- }
-
- public void setRoles(List roles, User user) {
- permissions.setItems(roles);
- user.getMatrix().stream().forEach(authorizationMatrix -> {
- permissions.select(authorizationMatrix.getRole());
- });
- }
-
- public boolean hasPasswordChanged(User user) {
- return !originalPassword.equals(user.getPassword());
- }
-
- public abstract static class UserFormEvent extends ComponentEvent {
- private User user;
-
- protected UserFormEvent(UserForm source, User user) {
- super(source, false);
- this.user = user;
- }
-
- public User getUser() {
- return user;
- }
- }
-
- public static class SaveEvent extends UserFormEvent {
- SaveEvent(UserForm source, User user) {
- super(source, user);
- }
- }
-
- public static class DeleteEvent extends UserFormEvent {
- DeleteEvent(UserForm source, User user) {
- super(source, user);
- }
- }
-
- public static class CloseEvent extends UserFormEvent {
- CloseEvent(UserForm source) {
- super(source, null);
- }
- }
-
- public void addDeleteListener(ComponentEventListener listener) {
- addListener(DeleteEvent.class, listener);
- }
-
- public void addSaveListener(ComponentEventListener listener) {
- addListener(SaveEvent.class, listener);
- }
-
- public void addCloseListener(ComponentEventListener listener) {
- addListener(CloseEvent.class, listener);
- }
-}
diff --git a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserView.java b/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserView.java
deleted file mode 100644
index 9f981ec..0000000
--- a/kontor-spring/src/main/java/de/thpeetz/kontor/admin/views/UserView.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package de.thpeetz.kontor.admin.views;
-
-import com.vaadin.flow.component.Component;
-import com.vaadin.flow.component.button.Button;
-import com.vaadin.flow.component.grid.Grid;
-import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
-import com.vaadin.flow.component.orderedlayout.VerticalLayout;
-import com.vaadin.flow.component.textfield.TextField;
-import com.vaadin.flow.data.value.ValueChangeMode;
-import com.vaadin.flow.router.PageTitle;
-import com.vaadin.flow.router.Route;
-import com.vaadin.flow.spring.annotation.SpringComponent;
-import de.thpeetz.kontor.admin.data.Role;
-import de.thpeetz.kontor.admin.data.User;
-import de.thpeetz.kontor.admin.services.KontorUserDetailsService;
-import de.thpeetz.kontor.common.views.MainLayout;
-import jakarta.annotation.security.RolesAllowed;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Scope;
-import org.springframework.security.crypto.password.PasswordEncoder;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-@Slf4j
-@SpringComponent
-@Scope("prototype")
-@RolesAllowed("ROLE_ADMIN")
-@Route(value = "admin/user", layout = MainLayout.class)
-@PageTitle("User | Admin | Kontor")
-public class UserView extends VerticalLayout {
-
- Grid grid = new Grid<>(User.class);
- TextField filterText = new TextField();
- UserForm form;
- KontorUserDetailsService service;
-
- @Autowired
- PasswordEncoder passwordEncoder;
-
- public UserView(KontorUserDetailsService service) {
- this.service = service;
- addClassName("user-view");
- setSizeFull();
- configureGrid();
- configureForm();
-
- add(getToolbar(), getContent());
- updateList();
- }
-
- private void configureGrid() {
- grid.addClassName("user-grid");
- grid.setSizeFull();
- grid.setColumns("userName", "email", "firstName", "lastName", "enabled");
- grid.getColumns().forEach(col -> col.setAutoWidth(true));
- grid.asSingleSelect().addValueChangeListener(event -> editUser(event.getValue()));
- }
-
- private void configureForm() {
- form = new UserForm();
- form.setWidth("25em");
- form.setVisible(false);
- form.addSaveListener(this::saveUser);
- form.addDeleteListener(this::deleteUser);
- form.addCloseListener(e -> closeEditor());
- }
-
- private void saveUser(UserForm.SaveEvent event) {
- User user = event.getUser();
- log.debug("UserView.saveUser: {}", user);
- List permissions = form.permissions.getSelectedItems().stream().collect(Collectors.toList());
- log.info("selected permissions: {}", permissions);
- if (form.hasPasswordChanged(user)) {
- user.setPassword(passwordEncoder.encode(user.getPassword()));
- log.debug("password changed for user {}", user);
- }
- service.saveUser(user, permissions);
- updateList();
- closeEditor();
- }
-
- private void deleteUser(UserForm.DeleteEvent event) {
- service.deleteUser(event.getUser());
- updateList();
- closeEditor();
- }
-
- private Component getContent() {
- HorizontalLayout content = new HorizontalLayout(grid, form);
- content.setFlexGrow(2, grid);
- content.setFlexGrow(1, form);
- content.addClassName("content");
- content.setSizeFull();
- return content;
- }
-
- private HorizontalLayout getToolbar() {
- filterText.setPlaceholder("Filter by user name...");
- filterText.setClearButtonVisible(true);
- filterText.setValueChangeMode(ValueChangeMode.LAZY);
- filterText.addValueChangeListener(e -> updateList());
- Button addUserButton = new Button("Add user", click -> addUser());
- HorizontalLayout toolbar = new HorizontalLayout(filterText, addUserButton);
- toolbar.addClassName("toolbar");
- return toolbar;
- }
-
- public void editUser(User user) {
- if (user == null) {
- closeEditor();
- } else {
- form.setUser(user);
- form.setRoles(service.findAllRoles(), user);
- form.setVisible(true);
- addClassName("editing");
- }
- }
-
- public void closeEditor() {
- form.setUser(null);
- form.setVisible(false);
- removeClassName("editing");
- }
-
- private void addUser() {
- grid.asSingleSelect().clear();
- editUser(new User());
- }
-
- private void updateList() {
- grid.setItems(service.findAllUsers(filterText.getValue()));
- }
-}
diff --git a/kontor-spring/src/main/resources/application.yml b/kontor-spring/src/main/resources/application.yml
index 998c19b..c4abf9b 100644
--- a/kontor-spring/src/main/resources/application.yml
+++ b/kontor-spring/src/main/resources/application.yml
@@ -11,10 +11,10 @@ spring:
hibernate:
ddl-auto: update
#ddl-auto: create-drop
- show-sql: true
+ show-sql: false
properties:
hibernate:
- dialect: org.hibernate.dialect.PostgreSQLDialect
+ #dialect: org.hibernate.dialect.PostgreSQLDialect
sql:
init:
mode: never