166 Commits

Author SHA1 Message Date
Thomas Peetz 2cfaaa4d87 remove obsolete kontor.py
fixes #15
2025-04-30 17:28:09 +02:00
Thomas Peetz 5850b1cfb0 rename User to Profile and Role to Permission. refs #15 2025-04-30 17:22:40 +02:00
Thomas Peetz 8833d2ca6a add db-password.txt in preparation of switching to PostgreSQL 2025-04-29 14:48:31 +02:00
Thomas Peetz 1290a45fb5 kontor-api: Add details pages for Comics, Artists and MediaFiles 2025-04-28 15:47:07 +02:00
Thomas Peetz 3afbbf900d add life of MediaFile 2025-04-27 22:11:56 +02:00
tpeetz f71419d37b Merge branch 'feature/12-import-templates-from-bottle' into 'develop/0.1.0'
Resolve "import templates from  bottle"

Closes #12

See merge request tpeetz/kontor!16
2025-04-27 14:53:34 +02:00
Thomas Peetz f5f673fd4e add comic artist details page 2025-04-27 14:52:07 +02:00
Thomas Peetz 8ec0784490 import templates from bottle and django. Use bootstrap for HTML content 2025-04-27 14:14:56 +02:00
Thomas Peetz 13faff8e01 add dependency to mariadb for kontor and kontor-api 2025-04-26 22:57:52 +02:00
Thomas Peetz 0a4f6bb0f9 remove kontor-cli 2025-04-26 22:17:09 +02:00
Thomas Peetz 3b4e5634b1 remove scriptsa.bak 2025-04-26 00:40:00 +02:00
Thomas Peetz 8c98e6de26 kontor-spring: change IssueView to use StatusIcon 2025-04-25 16:32:31 +02:00
Thomas Peetz 525d82cbda remove conflict marker 2025-04-25 08:13:33 +02:00
Thomas Peetz e26aaa1420 fix deprecatino warning in build.gradle 2025-04-25 07:47:29 +02:00
tpeetz ec7f05d8fc Merge branch 'feature/10-improve-docker-image' into 'develop/0.1.0'
Feature/10 improve docker image

See merge request tpeetz/kontor!14
2025-04-25 01:21:16 +02:00
Thomas Peetz 727e95e732 setup kontor-schema 2025-04-25 01:19:59 +02:00
tpeetz 75d3bf6ffb Resolve "evaluate uv" 2025-04-25 01:19:59 +02:00
Thomas Peetz 94782fc468 relocate sources 2025-04-25 01:19:59 +02:00
tpeetz 101ece44d8 Resolve "evaluate uv" 2025-04-25 01:19:59 +02:00
Thomas Peetz e880594a5b change schema back to use BIT(1) for boolean 2025-04-25 01:19:59 +02:00
Thomas Peetz c222d4cd7a copy sources from branch develop/0.1.0 2025-04-25 01:19:59 +02:00
Thomas Peetz cb0fa3f728 change schema 2025-04-25 01:19:59 +02:00
Thomas Peetz 4709e431b7 add schema from kontor-schema 2025-04-25 01:19:59 +02:00
Thomas Peetz bfccca72a1 copy schema to kontor-api 2025-04-25 01:19:59 +02:00
Thomas Peetz 8a3eebaab5 move schema to separate uv project 2025-04-25 01:19:47 +02:00
Thomas Peetz 6716103d0c setup kontor-schema 2025-04-25 01:19:37 +02:00
Thomas Peetz ee78af1abe relocate sources 2025-04-25 01:19:28 +02:00
tpeetz 1fc726ee4b Resolve "evaluate uv" 2025-04-25 01:19:28 +02:00
Thomas Peetz 882f48de0b relocate sources 2025-04-25 01:19:14 +02:00
tpeetz 097a3efd4a Merge branch 'feature/11-evaluate-uv' into 'develop/0.1.0'
Resolve "evaluate uv"

Closes #11

See merge request tpeetz/kontor!15
2025-04-21 17:39:21 +02:00
tpeetz 934ef826c9 Resolve "evaluate uv" 2025-04-21 17:39:20 +02:00
Thomas Peetz bb049441b4 extend check_kontor.py for comparing json file with db 2025-04-21 00:55:50 +02:00
tpeetz 2bdbef8f8a change boolean fields in schema from Bit(1) to Boolean 2025-04-19 01:40:13 +02:00
tpeetz 20f29d88fa add script to copy from json to mariadb 2025-04-18 22:55:40 +02:00
Thomas Peetz 1997957069 remove .itlab-ci.yml and call of pytest to Makefile 2025-04-18 00:52:00 +02:00
Thomas Peetz 116bb77e4c update check_kontor.py to call Kontor API 2025-04-18 00:09:30 +02:00
Thomas Peetz a6eeea6c1f add testing to fastapi 2025-04-17 02:21:14 +02:00
Thomas Peetz 4a61d6a727 refactor scripts to work wit api 2025-04-16 05:08:59 +02:00
Thomas Peetz 98e3d91edd reorganize python projects 2025-04-15 01:29:08 +02:00
tpeetz a169f6a6c1 Merge branch 'feature/9-evaluate-sqlmodel' into 'develop/0.1.0'
evaluate sqlmodel

See merge request tpeetz/kontor!13
2025-04-13 16:16:10 +02:00
tpeetz b14a267b5b evaluate sqlmodel 2025-04-13 16:16:10 +02:00
Thomas Peetz a43e2c806c add virtualenv to scripts 2025-04-08 12:14:20 +02:00
Thomas Peetz 10834df92b add script for import data 2025-04-08 11:49:52 +02:00
Thomas Peetz 3d97c3360a add MetaDataColumn import 2025-04-07 22:26:33 +02:00
Thomas Peetz 76d91dd506 add import view 2025-04-07 19:34:08 +02:00
Thomas Peetz 84aff2b7d7 add data for cards 2025-04-06 09:41:53 +02:00
Thomas Peetz 2f8c10a692 add package repository and refactor classes 2025-04-03 22:31:11 +02:00
Thomas Peetz c5f74e007a organize imports 2025-04-01 23:13:08 +02:00
Thomas Peetz 243f693f9d ignore project files 2025-04-01 23:08:54 +02:00
Thomas Peetz 976b4f3f47 extract ColumnToggleContextMenu and createStatusIcon to separate classes 2025-04-01 22:09:41 +02:00
Thomas Peetz 1b18dae311 add scripts from repository python-scripts 2025-04-01 08:12:53 +02:00
Thomas Peetz 38e77b25b1 change display of boolean in ComicView and add docker tasks to build.gradle 2025-03-31 22:41:08 +02:00
tpeetz bbc08863f0 Merge branch 'feature/8-create-docker-build' into 'develop/0.1.0'
Resolve "Create docker build"

Closes #8

See merge request tpeetz/kontor!6
2025-03-31 18:40:40 +02:00
tpeetz d58f18d206 Merge branch 'develop/0.1.0' into 'feature/8-create-docker-build'
# Conflicts:
#   springboot/Dockerfile
2025-03-31 18:30:11 +02:00
Thomas Peetz fd5bc54eee import repositories kontor-java and kontor-ee 2025-03-30 15:57:09 +02:00
Thomas Peetz cbc57d22f8 add Docker build and domcker-compose file 2025-03-30 04:17:58 +02:00
Thomas Peetz a5cdf8867a move Git repository kontor-docker to directory bottle-docker 2025-03-29 19:05:31 +01:00
tpeetz 45971518ee change ModuleData view 2025-03-17 18:43:55 +01:00
Thomas Peetz 7db244f599 add length vor varchar for _id fields 2025-02-12 18:07:48 +01:00
Thomas Peetz 41e2b11da2 add details view 2025-02-11 16:16:38 +01:00
Thomas Peetz 0d1b2e416e add tab witg table and details 2025-02-11 10:39:07 +01:00
Thomas Peetz d8eecb4dab fix NPE in MediaActorView 2025-02-10 15:56:32 +01:00
Thomas Peetz 3bb3b22cea add MediaActor and MediaActorFile to kontor-schema 2025-02-10 12:54:59 +01:00
Thomas Peetz 8ffd421c1b add views for MediaActor and adapt MediaFile 2025-02-10 12:25:39 +01:00
Thomas Peetz 6d54c7f315 add MediaActor 2025-02-10 00:27:23 +01:00
Thomas Peetz f5fb743503 refactor kontor-schema 2025-02-08 20:22:21 +01:00
tpeetz ca9022d289 Merge branch 'table_sortable' into 'develop/0.1.0'
Table sortable

See merge request tpeetz/kontor!11
2025-02-06 15:59:29 +00:00
Thomas Peetz ed0e108599 refactor project by using enums for recurring strings 2025-02-06 16:48:41 +01:00
Thomas Peetz 7dc18b10cb refactor kontor-schema by moving classes to seprate modules 2025-02-06 16:44:14 +01:00
Thomas Peetz 171bc1676a refactor project by using enums for recurring strings 2025-02-06 16:33:15 +01:00
Thomas Peetz 1a5cd6ffe8 refactor kontor-schema by moving classes to seprate modules 2025-02-06 11:09:09 +01:00
Thomas Peetz 71ecfaff1f make tables sortable 2025-02-03 23:28:35 +01:00
Thomas Peetz f33aaadce7 fix problem with closing subwindows and define field length for id in schema 2025-02-03 17:37:19 +01:00
Thomas Peetz 591171b223 add meta data subwindow 2025-02-02 21:38:16 +01:00
Thomas Peetz da453d642d rename kontor.py to main.py for pyside6-deploy 2025-01-28 23:19:23 +01:00
Thomas Peetz e733fa21e6 moved update and download functionality to kontor-schema 2025-01-28 15:10:10 +01:00
Thomas Peetz c61e49720e update title 2025-01-27 22:42:32 +01:00
Thomas Peetz d01489b1fa reformat python file 2025-01-27 15:21:51 +01:00
Thomas Peetz 93c7498a83 setting return value of adding link to db 2025-01-27 15:20:35 +01:00
Thomas Peetz fe89cc6e0f add Mixin for videos 2025-01-27 13:34:15 +01:00
Thomas Peetz 1e9ca7c1a4 add Enum for Videotype 2025-01-25 03:53:47 +01:00
Thomas Peetz 400aff6524 Add MediaWindow 2025-01-24 08:58:59 +01:00
Thomas Peetz 845f085a76 remove data.json 2025-01-24 05:31:20 +01:00
Thomas Peetz 88c623edb7 subclass VideoFile 2025-01-24 05:02:08 +01:00
Thomas Peetz 0cc73c09aa remove pyproject.toml 2025-01-21 20:14:27 +01:00
Thomas Peetz e8660faa06 Refactor GUI to use Multi Document Interface 2025-01-21 20:12:50 +01:00
Thomas Peetz 4e884fdbe5 fix exporting and importing from file 2025-01-21 16:46:12 +01:00
Thomas Peetz 4c330a1138 remove kontor-cli.backup 2025-01-21 09:42:33 +01:00
Thomas Peetz 65288a53a1 complete MetaDataTable and MetaDataColumn 2025-01-20 15:40:45 +01:00
Thomas Peetz 60fba0d3e9 add dependencies to kontor-schema and kontor-video in rquirements.txt 2025-01-19 23:39:53 +01:00
Thomas Peetz ada723dc48 separate cli and gui application in own python packages. provide database schema as python package. 2025-01-19 23:36:52 +01:00
Thomas Peetz f07c7b74ee prepare progress bar for download 2025-01-19 17:49:13 +01:00
Thomas Peetz f3c59c11ba add progress in statusbar 2025-01-18 01:28:27 +01:00
Thomas Peetz 3c4d3ad326 add check files command 2025-01-17 17:19:31 +01:00
Thomas Peetz 917e287784 fix missing argument for KontorDB in import_cmd and export 2025-01-17 13:38:16 +01:00
Thomas Peetz 611d6b9e61 add update and download for MediaFile 2025-01-17 13:31:49 +01:00
Thomas Peetz 58263cb854 use sqlalchemy session only as short as possible 2025-01-16 23:03:30 +01:00
Thomas Peetz 33dcbc4413 add command to add link 2025-01-16 16:50:14 +01:00
Thomas Peetz f3cf1a17f3 add import functionality 2025-01-15 14:16:35 +01:00
Thomas Peetz 3466c10a88 add commands and subcommands 2025-01-14 22:58:15 +01:00
Thomas Peetz 4cea554116 add missing schema classes 2025-01-14 17:53:05 +01:00
tpeetz 332e09b283 Merge branch 'feature/5-add-token-management' into 'develop/0.1.0'
Resolve "Add token management"

Closes #5

See merge request tpeetz/kontor!3
2025-01-14 14:55:54 +00:00
tpeetz d3c720f995 Merge branch 'develop/0.1.0' into 'feature/5-add-token-management'
# Conflicts:
#   qt/gui/main_window.py
2025-01-14 14:44:46 +00:00
Thomas Peetz 453ad2af7b merged command line and gui app in one command 2025-01-14 15:39:07 +01:00
Thomas Peetz 54bc17ee7d add cli app and fix relationship typos 2025-01-14 15:39:06 +01:00
Thomas Peetz 89b7b87b8c add export to json 2025-01-14 15:39:03 +01:00
Thomas Peetz 3f0a37ff19 create files with abstract model class 2025-01-14 15:38:30 +01:00
Thomas Peetz 954dab289a extend MetaDataColumn by adding column for name of column for referenced tables 2025-01-14 15:38:30 +01:00
Thomas Peetz 5affa16505 add tysc schema 2025-01-14 15:38:30 +01:00
Thomas Peetz d2b1cb999a reorganize files for Qt application 2025-01-14 15:38:27 +01:00
Thomas Peetz f1d36acff7 add sqlalchemy as orm tool 2025-01-14 15:35:53 +01:00
Thomas Peetz 7e8a6b3c91 add repo kontor-quarkus 2025-01-14 15:35:53 +01:00
Thomas Peetz b424e95e05 import other kontor repos into directories 2025-01-14 15:35:53 +01:00
Thomas Peetz 28746adfbb import from kontor-flask branch develop/0.1.0 2025-01-14 15:35:53 +01:00
Thomas Peetz 0d2f27f771 import from kontor-flask 2025-01-14 15:35:53 +01:00
Thomas Peetz d6410e2584 implement generic table model
implement generic table model which reads table info from db and
constructs table view
2025-01-14 15:21:03 +01:00
Thomas Peetz ce1514f20a fix errors when displaying empty table 2025-01-14 15:18:49 +01:00
Thomas Peetz c8ea7c4188 refresh table when updated 2025-01-14 15:18:46 +01:00
Thomas Peetz 245ee2378e display tick and cross for boolean values 2025-01-14 15:17:41 +01:00
Thomas Peetz afb3ac88c8 first implementation to show Comics and MediaFiles 2025-01-14 15:17:37 +01:00
Thomas Peetz 3a0a0055a6 Add Taskjuggler project file 2025-01-14 15:13:59 +01:00
tpeetz 283577df9b Merge branch 'add-cli-app' into 'develop/0.1.0'
Add cli app

See merge request tpeetz/kontor!9
2025-01-14 12:22:53 +00:00
Thomas Peetz 276302570f merged command line and gui app in one command 2025-01-14 13:10:24 +01:00
Thomas Peetz f74c07af9a add cli app and fix relationship typos 2025-01-13 22:54:25 +01:00
Thomas Peetz d0eae1980a add export to json 2025-01-13 16:18:13 +01:00
Thomas Peetz 820ae3d374 create files with abstract model class 2025-01-13 00:26:42 +01:00
Thomas Peetz bf14c9a020 extend MetaDataColumn by adding column for name of column for referenced tables 2025-01-12 02:41:40 +01:00
Thomas Peetz a3652cc9b8 add tysc schema 2025-01-11 19:00:58 +01:00
Thomas Peetz 8f2e99195a reorganize files for Qt application 2025-01-11 01:54:05 +01:00
Thomas Peetz 2ae11e24ef add sqlalchemy as orm tool 2025-01-10 17:39:54 +01:00
Thomas Peetz a1e8474149 add repo kontor-quarkus 2025-01-09 22:28:46 +01:00
Thomas Peetz 9b329c0ac4 import other kontor repos into directories 2025-01-09 19:28:50 +01:00
tpeetz 8120c92b32 Merge branch 'feature/4-import-kontor-flask-into-directory-flask' into 'develop/0.1.0'
Import kontor-flask into directory flask

Closes #4

See merge request tpeetz/kontor!2
2025-01-08 21:44:47 +00:00
Thomas Peetz 5c261529fc import from kontor-flask branch develop/0.1.0 2025-01-08 22:34:21 +01:00
Thomas Peetz 6923b3e5ee import from kontor-flask 2025-01-08 22:31:20 +01:00
Thomas Peetz fc4110b11d fix problem in string formatting 2025-01-08 22:25:44 +01:00
tpeetz e078f43cc6 set correct project id in build.gradle 2025-01-08 22:25:44 +01:00
Thomas Peetz 24e4c0b58e import export dialog by adding selection of export type 2025-01-08 22:25:44 +01:00
Thomas Peetz 8d31a92692 improve view of meta data by using icons for boolean and add search 2025-01-08 22:25:44 +01:00
Thomas Peetz c619745a4a prevent NPE 2025-01-08 22:25:44 +01:00
Thomas Peetz c769115331 implement general data tab view 2025-01-08 22:25:44 +01:00
Thomas Peetz 1a7da0ab9f add column header info 2025-01-08 22:25:44 +01:00
tpeetz 78632e0e12 Import kontor-spring into directory springboot 2025-01-08 22:25:44 +01:00
Thomas Peetz 3aed8af868 implement generic table model
implement generic table model which reads table info from db and
constructs table view
2025-01-08 22:25:44 +01:00
Thomas Peetz c6d1e4d7e7 fix errors when displaying empty table 2025-01-08 22:25:44 +01:00
Thomas Peetz d98dc79cf8 refresh table when updated 2025-01-08 22:25:44 +01:00
Thomas Peetz 9dcc09c586 display tick and cross for boolean values 2025-01-08 22:25:44 +01:00
Thomas Peetz 57e7b9e999 first implementation to show Comics and MediaFiles 2025-01-08 22:25:44 +01:00
Thomas Peetz d97145b629 add first implementation of Dockerfile 2025-01-08 21:57:13 +01:00
Thomas Peetz 8ba23b6365 add first example of a Dockerfile for a Spring Boot Application 2025-01-08 21:41:20 +01:00
Thomas Peetz 66d61e2c1f add entity token 2025-01-08 21:08:15 +01:00
Thomas Peetz 83f645d4c6 fix problem in string formatting 2025-01-08 15:34:11 +01:00
tpeetz 7d84e341d6 set correct project id in build.gradle 2025-01-08 14:28:18 +00:00
Thomas Peetz c3ec0977c0 import export dialog by adding selection of export type 2025-01-08 14:15:29 +01:00
Thomas Peetz d51bc47ffe improve view of meta data by using icons for boolean and add search 2025-01-08 10:39:52 +01:00
Thomas Peetz faa1d73eea prevent NPE 2025-01-08 08:05:09 +01:00
Thomas Peetz fe407d6d89 implement general data tab view 2025-01-07 21:18:14 +01:00
Thomas Peetz 0759c99c1d add column header info 2025-01-07 01:48:03 +01:00
tpeetz 77bb86a9fa Merge branch 'feature/2-import-kontor-spring-into-directory-springboot' into 'develop/0.1.0'
Import kontor-spring into directory springboot

Closes #2

See merge request tpeetz/kontor!1
2025-01-06 17:09:38 +00:00
tpeetz 4d7d7391c3 Import kontor-spring into directory springboot 2025-01-06 17:09:38 +00:00
Thomas Peetz c5aa3b32ef implement generic table model
implement generic table model which reads table info from db and
constructs table view
2025-01-06 17:07:20 +01:00
Thomas Peetz 4ff999a74e fix errors when displaying empty table 2025-01-05 23:44:29 +01:00
Thomas Peetz bdf2b2ba43 refresh table when updated 2025-01-05 21:47:27 +01:00
Thomas Peetz 04dfe483e4 display tick and cross for boolean values 2025-01-05 14:13:15 +01:00
Thomas Peetz 4a279738e3 first implementation to show Comics and MediaFiles 2025-01-05 14:10:15 +01:00
Thomas Peetz 3f65ec55fc Add Taskjuggler project file 2024-12-05 20:40:13 +01:00
780 changed files with 8856 additions and 43952 deletions
-19
View File
@@ -1,19 +0,0 @@
name: Gitea Actions Demo
run-name: ${{ gitea.actor }} ist testing out Gitea Actions
on: [push]
jobs:
Explore-Gitea-Actions:
runs-on: inky
steps:
- run: echo "The job was automatically triggered by a ${{ gitea.event_name }} event."
- run: echo "This job is now running on a ${{ runner.os }} server hosted by Gitea!"
- run: echo "The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Check out repository code
uses: actions/checkout@v4
- run: echo "The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "The workflow is now ready to test your code on the runner."
- name: List files in the directory
run: |
ls ${{ gitea.workspace }}
- run: echo "The job's status is ${{ job.status }}."
+2 -6
View File
@@ -1,6 +1,6 @@
.vscode/
.idea/ .idea/
.angular/ .theia/
.vscode/
__pycache__/ __pycache__/
bonus/ bonus/
icons/ icons/
@@ -13,8 +13,4 @@ kontor-schema/kontor_schema.egg-info
kontor-gui/.pdm-python kontor-gui/.pdm-python
kontor-gui/dist kontor-gui/dist
fastapi/.coverage fastapi/.coverage
kontor-api/.coverage
db-password.txt db-password.txt
kontor-api/tests/test_main.py
kontor-api/tests/test_db.db
kontor-api/test_db.db
+2 -3
View File
@@ -1,10 +1,9 @@
kontor_api := kontor-api kontor_api := kontor-api
kontor_spring := kontor-spring kontor_spring := kontor-spring
kontor_servicemix := kontor-servicemix
.PHONY: all $(kontor_spring) $(kontor_api) .PHONY: all $(kontor_spring) $(kontor_api)
all: $(kontor_spring) $(kontor_api) $(kontor_servicemix) all: $(kontor_spring) $(kontor_api)
$(kontor_spring) $(kontor_api) $(kontor_servicemix): $(kontor_spring) $(kontor_api):
$(MAKE) --directory=$@ $(TARGET) $(MAKE) --directory=$@ $(TARGET)
-22
View File
@@ -1,22 +0,0 @@
services:
couchdb:
image: couchdb
restart: unless-stopped
environment:
- COUCHDB_USER=admin
- COUCHDB_PASSWORD=admin
ports:
- 5984:5984
networks:
- database
- frontend
volumes:
- couchdb-data:/opt/couchdb/data
secrets:
- couchdb-password
volumes:
couchdb-data:
secrets:
couchdb-password:
file: couchdb-password.txt
+20 -119
View File
@@ -1,143 +1,44 @@
services: services:
postgres: mariadb:
image: postgres image: mariadb
restart: unless-stopped restart: unless-stopped
environment: environment:
- POSTGRES_DB=kontor MYSQL_ROOT_PASSWORD: kontor
- POSTGRES_USER=kontor MYSQL_USER: kontor
#- POSTGRES_PASSWORD_FILE=/run/secrets/db-password MYSQL_PASSWORD: kontor
- POSTGRES_PASSWORD=kontor MYSQL_DATABASE: kontor
healthcheck:
test: ["CMD-SHELL", "pg_isready -U kontor"]
interval: 1s
timeout: 5s
retries: 10
ports: ports:
- 5432:5432 - 3316:3306
networks:
- integration
- frontend
volumes:
- postgres-data:/var/lib/postgresql/data:rw
secrets:
- db-password
adminer:
image: adminer
ports:
- 8090:8080
networks: networks:
- database - database
- frontend volumes:
- mariadb-storage:/var/lib/mysql:rw
kontor: kontor:
image: kontor:0.2.0-SNAPSHOT image: kontor
restart: unless-stopped restart: unless-stopped
networks: networks:
- database - database
- integration
- frontend - frontend
ports: ports:
- 8000:8000 - 8000:8000
volumes:
- images-data:/data/images
depends_on: depends_on:
postgres: - mariadb
condition: service_healthy
kontor-api: kontor-api:
image: kontor-api:0.2.0-SNAPSHOT image: kontor-api
restart: unless-stopped restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://kontor-api:8500/health"]
interval: 10s
timeout: 5s
retries: 3
networks: networks:
- database - database
- integration
- frontend
ports:
- 8500:8500
volumes:
- images-data:/data/images
depends_on:
postgres:
condition: service_healthy
kontor-fiber:
build:
context: ./kontor-fiber
dockerfile: Dockerfile
tags:
- kontor-fiber:0.2.0-SNAPSHOT
image: kontor-fiber:0.2.0-SNAPSHOT
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://kontor-fiber:8600/health"]
interval: 10s
timeout: 5s
retries: 3
networks:
- database
- integration
- frontend
ports:
- 8600:8600
depends_on:
postgres:
condition: service_healthy
kontor-echo:
build:
context: ./kontor-echo
dockerfile: Dockerfile
tags:
- kontor-echo:0.2.0-SNAPSHOT
image: kontor-echo:0.2.0-SNAPSHOT
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://kontor-echo:8700/health"]
interval: 10s
timeout: 5s
retries: 3
networks:
- database
- integration
- frontend
ports:
- 8700:8700
depends_on:
postgres:
condition: service_healthy
kontor-quarkus:
build:
context: ./kontor-quarkus
dockerfile: Dockerfile
tags:
- kontor-quarkus:0.2.0-SNAPSHOT
image: kontor-quarkus:0.2.0-SNAPSHOT
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://kontor-quarkus:8800/q/health"]
interval: 10s
timeout: 5s
retries: 3
networks:
- database
- integration
- frontend - frontend
ports: ports:
- 8800:8800 - 8800:8800
volumes:
- images-data:/data/images
depends_on: depends_on:
postgres: - mariadb
condition: service_healthy
networks: networks:
integration: database:
name: integration frontend:
frontend:
volumes: volumes:
postgres-data: mariadb-storage:
images-data:
secrets:
db-password:
file: db-password.txt
+9
View File
@@ -0,0 +1,9 @@
build/
.settings/
.gradle/
.classpath
.project
.vscode/
bin/
.idea/
/.eclipse-pmd
+38
View File
@@ -0,0 +1,38 @@
variables:
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
before_script:
- source "/home/gitlab-runner/.sdkman/bin/sdkman-init.sh"
- sdk u java 11.0.12-open
- chmod +x ./gradlew
stages:
- build
- test
- analysis
- publish
Build Application:
stage: build
script: ./gradlew --build-cache compileJava
Create Documentation:
stage: build
script: ./gradlew asciidoctorPdf
Test Application:
stage: test
script: ./gradlew check
sonarqube-check:
stage: analysis
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
script: ./gradlew sonarqube
allow_failure: true
Publish Artifacts:
stage: publish
script: ./gradlew publish
@@ -1,68 +1,60 @@
# kontor-quarkus # kontor-quarkus Project
This project uses Quarkus, the Supersonic Subatomic Java Framework. This project uses Quarkus, the Supersonic Subatomic Java Framework.
If you want to learn more about Quarkus, please visit its website: <https://quarkus.io/>. If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ .
## Running the application in dev mode ## Running the application in dev mode
You can run your application in dev mode that enables live coding using: You can run your application in dev mode that enables live coding using:
```shell script ```shell script
./gradlew quarkusDev ./gradlew quarkusDev
``` ```
> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at <http://localhost:8080/q/dev/>. > **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8080/q/dev/.
## Packaging and running the application ## Packaging and running the application
The application can be packaged using: The application can be packaged using:
```shell script ```shell script
./gradlew build ./gradlew build
``` ```
It produces the `quarkus-run.jar` file in the `build/quarkus-app/` directory. It produces the `quarkus-run.jar` file in the `build/quarkus-app/` directory.
Be aware that its not an _über-jar_ as the dependencies are copied into the `build/quarkus-app/lib/` directory. Be aware that its not an _über-jar_ as the dependencies are copied into the `build/quarkus-app/lib/` directory.
The application is now runnable using `java -jar build/quarkus-app/quarkus-run.jar`. The application is now runnable using `java -jar build/quarkus-app/quarkus-run.jar`.
If you want to build an _über-jar_, execute the following command: If you want to build an _über-jar_, execute the following command:
```shell script ```shell script
./gradlew build -Dquarkus.package.jar.type=uber-jar ./gradlew build -Dquarkus.package.type=uber-jar
``` ```
The application, packaged as an _über-jar_, is now runnable using `java -jar build/*-runner.jar`. The application, packaged as an _über-jar_, is now runnable using `java -jar build/*-runner.jar`.
## Creating a native executable ## Creating a native executable
You can create a native executable using: You can create a native executable using:
```shell script ```shell script
./gradlew build -Dquarkus.native.enabled=true ./gradlew build -Dquarkus.package.type=native
``` ```
Or, if you don't have GraalVM installed, you can run the native executable build in a container using: Or, if you don't have GraalVM installed, you can run the native executable build in a container using:
```shell script ```shell script
./gradlew build -Dquarkus.native.enabled=true -Dquarkus.native.container-build=true ./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true
``` ```
You can then execute your native executable with: `./build/kontor-quarkus-1.0.0-SNAPSHOT-runner` You can then execute your native executable with: `./build/kontor-quarkus-1.0.0-SNAPSHOT-runner`
If you want to learn more about building native executables, please consult <https://quarkus.io/guides/gradle-tooling>. If you want to learn more about building native executables, please consult https://quarkus.io/guides/gradle-tooling.
## Related Guides ## Related Guides
- REST ([guide](https://quarkus.io/guides/rest)): A Jakarta REST implementation utilizing build time processing and Vert.x. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it. - RESTEasy Reactive ([guide](https://quarkus.io/guides/resteasy-reactive)): A JAX-RS implementation utilizing build time processing and Vert.x. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it.
- REST Jackson ([guide](https://quarkus.io/guides/rest#json-serialisation)): Jackson serialization support for Quarkus REST. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it
- JDBC Driver - PostgreSQL ([guide](https://quarkus.io/guides/datasource)): Connect to the PostgreSQL database via JDBC
## Provided Code ## Provided Code
### REST ### RESTEasy Reactive
Easily start your REST Web Services Easily start your Reactive RESTful Web Services
[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources) [Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources)
+158
View File
@@ -0,0 +1,158 @@
plugins {
id 'java'
id 'maven-publish'
id 'jacoco'
id 'jacoco-report-aggregation'
id 'pmd'
id 'checkstyle'
id 'jvm-test-suite'
id 'io.quarkus'
id "org.sonarqube" version "3.3"
alias(versionsLibs.plugins.asciidoctorConvert)
alias(versionsLibs.plugins.asciidoctorPdf)
alias(versionsLibs.plugins.asciidoctorGems)
}
repositories {
ruby.gems()
maven {
url "https://nexus.thpeetz.de/nexus/content/groups/public/"
}
mavenCentral()
mavenLocal()
}
dependencies {
implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
implementation 'io.quarkus:quarkus-resteasy-reactive'
implementation 'io.quarkus:quarkus-resteasy-reactive-jackson'
implementation 'io.quarkus:quarkus-arc'
testImplementation 'io.quarkus:quarkus-junit5'
testImplementation 'io.rest-assured:rest-assured'
testImplementation("io.quarkus:quarkus-jacoco")
}
final BUILD_DATE = new Date().format('dd.MM.yyyy').toString()
def pdfFile = layout.buildDirectory.file("docs/asciidocPdf/" + project.name + ".pdf")
def pdfArtifact = artifacts.add('archives', pdfFile.get().asFile) {
type 'pdf'
builtBy asciidoctorPdf
}
tasks.withType(GenerateModuleMetadata).configureEach {
suppressedValidationErrors.add('enforced-platform')
}
publishing {
publications {
quarkusApp(MavenPublication){
from components.java
}
docs(MavenPublication) {
groupId = group + '.docs'
artifactId = project.name
artifact pdfArtifact
}
}
repositories {
maven {
name = 'nexusPeetz'
def releasesRepoUrl = "https://nexus.thpeetz.de/nexus/content/repositories/releases/"
def snapshotsRepoUrl = "https://nexus.thpeetz.de/nexus/content/repositories/snapshots/"
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials(PasswordCredentials)
}
}
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
tasks.withType(GenerateModuleMetadata).configureEach {
suppressedValidationErrors.add('enforced-platform')
}
compileJava {
options.encoding = 'UTF-8'
options.compilerArgs << '-parameters'
}
compileTestJava {
options.encoding = 'UTF-8'
}
jacocoTestReport {
reports {
xml.required = true
}
}
test.finalizedBy jacocoTestReport
pmd {
//ruleSetFiles = files("custom-pmd-ruleset.xml")
ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]
ignoreFailures = true
}
tasks.withType(Checkstyle) {
reports {
xml.required = true
html.required = true
}
}
testing {
suites {
test {
useJUnitJupiter()
}
}
}
pmdTest.dependsOn("compileQuarkusTestGeneratedSourcesJava")
checkstyleTest.dependsOn("compileQuarkusTestGeneratedSourcesJava")
jacocoTestReport.dependsOn("compileQuarkusGeneratedSourcesJava")
pmdMain.dependsOn("compileQuarkusGeneratedSourcesJava")
sonarqube {
properties {
property "sonar.host.url", "https://sonar.thpeetz.de"
property "sonar.qualitygate.wait", true
property "sonar.sourceEncoding", "UTF-8"
property "sonar.projectKey", "kontor_kontor-quarkus_AYQek8Mxz0hBjLSV8I8O"
property "sonar.login", "5ecd90dee57806857e07443a9b0efd3cd7774a81"
}
}
tasks.named('sonarqube').configure {
dependsOn 'test', 'pmdMain', 'checkstyleMain', 'jacocoTestReport'
}
asciidoctorPdf {
baseDirFollowsSourceFile()
asciidoctorj {
attributes 'build-gradle': file('build.gradle'),
'endpoint-url': 'https://www.thpeetz.de',
'source-highlighter': 'rouge',
'imagesdir': './images',
'toc': 'left',
'toc-title': 'Inhaltsverzeichnis',
'revdate': BUILD_DATE,
'revnumber': { project.version.toString() },
'revremark': 'Entwurf',
'chapter-label': '',
'icons': 'font',
'idprefix': 'id_',
'idseparator': '-',
'docinfo1': ''
}
}
wrapper {
gradleVersion = "7.5"
}
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" "https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<module name="TreeWalker">
<module name="UnusedImports" />
<module name="AvoidStarImport" />
<module name="ConstantName" />
<module name="NeedBraces" />
<module name="RightCurly" />
<module name="ModifierOrder">
<property name="severity" value="warning"/>
</module>
</module>
</module>
+9
View File
@@ -0,0 +1,9 @@
#Gradle properties
group=de.thpeetz.kontor
version=1.0.0-SNAPSHOT
quarkusPluginId=io.quarkus
quarkusPluginVersion=2.13.2.Final
quarkusPlatformGroupId=io.quarkus.platform
quarkusPlatformArtifactId=quarkus-bom
quarkusPlatformVersion=2.13.2.Final
Binary file not shown.
@@ -1,7 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
+185
View File
@@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
+34 -24
View File
@@ -13,10 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@@ -27,8 +25,7 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@@ -43,13 +40,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if "%ERRORLEVEL%" == "0" goto init
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail goto fail
@@ -57,36 +54,49 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto init
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH= set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd if "%ERRORLEVEL%"=="0" goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL% if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
if %EXIT_CODE% equ 0 set EXIT_CODE=1 exit /b 1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal
@@ -0,0 +1,84 @@
= Projektbeschreibung kontor-quarkus: Pflichtenheft und Projekthandbuch
:author: Thomas Peetz
:email: <thomas.peetz@thpeetz.de>
:doctype: article
:sectnums:
:sectnumlevels: 4
:toc:
:toclevels: 4
:table-caption!:
:counter: table-number: 0
//[title="Dokumenthistorie", caption="Tabelle {counter:table-number} ", id="Tabelle-{counter:table-number}", options="header"]
//[title="Dokumenthistorie", id="Table-{counter:table-number}", options="header", cols="4"]
[title="Dokumenthistorie", id="Table-{counter:table-number}", options="header"]
|===
| Version | Datum | Autor | Änderungsgrund / Bemerkungen
| 0.0.1 | 16.05.2022 | Thomas Peetz | Ersterstellung
|===
== Einführung
=== Zweck
=== Stakeholder des Systems
=== Systemumfang
==== Zielsetzung des Systems
=== Systemübersicht
==== Systemkontext
==== Systemarchitektur
==== Systemschnittstellen
===== Realisierte Schnittstellen
===== Verwendete Schnittstellen
==== Logisches Datenmodell
==== Einschränkungen
== Anforderungen der Domäne
=== Systemfunktionen
==== Anwendungsfälle
==== Akteure
==== Zielgruppen
=== Anforderungen
==== Anforderungen an externe Schnittstellen
==== Funktionale Anforderungen
==== Qualitätsanforderungen
==== Randbedingungen
==== Weitere Anforderungen
==== Wartungs- und Supportinformationen
=== Verifikation
[bibliography]
== Referenzen
[glossary]
== Glossar
== Verzeichnisse
=== Abbildungsverzeichnis
=== Tabellenverzeichnis
<<Table-1, Tabelle 1>> <<Table-1>>
@@ -14,9 +14,7 @@
# docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus-jvm # docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus-jvm
# #
# If you want to include the debug port into your docker image # If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. # you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5005
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
# when running the container
# #
# Then run the container using : # Then run the container using :
# #
@@ -26,8 +24,7 @@
# This scripts computes the command line to execute your Java application, and # This scripts computes the command line to execute your Java application, and
# includes memory/GC tuning. # includes memory/GC tuning.
# You can configure the behavior using the following environment properties: # You can configure the behavior using the following environment properties:
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override # - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
# the default JVM options, use `JAVA_OPTS_APPEND` to append options
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options # - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
# in JAVA_OPTS (example: "-Dsome.property=foo") # in JAVA_OPTS (example: "-Dsome.property=foo")
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is # - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
@@ -78,7 +75,7 @@
# accessed directly. (example: "foo.example.com,bar.example.com") # accessed directly. (example: "foo.example.com,bar.example.com")
# #
### ###
FROM registry.access.redhat.com/ubi9/openjdk-21:1.21 FROM registry.access.redhat.com/ubi8/openjdk-11:1.14
ENV LANGUAGE='en_US:en' ENV LANGUAGE='en_US:en'
@@ -91,8 +88,7 @@ COPY --chown=185 build/quarkus-app/quarkus/ /deployments/quarkus/
EXPOSE 8080 EXPOSE 8080
USER 185 USER 185
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" ENV AB_JOLOKIA_OFF=""
ENV JAVA_OPTS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
@@ -3,7 +3,7 @@
# #
# Before building the container image run: # Before building the container image run:
# #
# ./gradlew build -Dquarkus.package.jar.type=legacy-jar # ./gradlew build -Dquarkus.package.type=legacy-jar
# #
# Then, build the image with: # Then, build the image with:
# #
@@ -14,9 +14,7 @@
# docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus-legacy-jar # docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus-legacy-jar
# #
# If you want to include the debug port into your docker image # If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. # you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5005
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
# when running the container
# #
# Then run the container using : # Then run the container using :
# #
@@ -26,8 +24,7 @@
# This scripts computes the command line to execute your Java application, and # This scripts computes the command line to execute your Java application, and
# includes memory/GC tuning. # includes memory/GC tuning.
# You can configure the behavior using the following environment properties: # You can configure the behavior using the following environment properties:
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override # - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
# the default JVM options, use `JAVA_OPTS_APPEND` to append options
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options # - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
# in JAVA_OPTS (example: "-Dsome.property=foo") # in JAVA_OPTS (example: "-Dsome.property=foo")
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is # - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
@@ -78,7 +75,7 @@
# accessed directly. (example: "foo.example.com,bar.example.com") # accessed directly. (example: "foo.example.com,bar.example.com")
# #
### ###
FROM registry.access.redhat.com/ubi9/openjdk-21:1.21 FROM registry.access.redhat.com/ubi8/openjdk-11:1.14
ENV LANGUAGE='en_US:en' ENV LANGUAGE='en_US:en'
@@ -88,7 +85,6 @@ COPY build/*-runner.jar /deployments/quarkus-run.jar
EXPOSE 8080 EXPOSE 8080
USER 185 USER 185
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" ENV AB_JOLOKIA_OFF=""
ENV JAVA_OPTS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
@@ -3,7 +3,7 @@
# #
# Before building the container image run: # Before building the container image run:
# #
# ./gradlew build -Dquarkus.native.enabled=true # ./gradlew build -Dquarkus.package.type=native
# #
# Then, build the image with: # Then, build the image with:
# #
@@ -13,17 +13,15 @@
# #
# docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus # docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus
# #
# The ` registry.access.redhat.com/ubi9/ubi-minimal:9.5` base image is based on UBI 9.
# To use UBI 8, switch to `quay.io/ubi8/ubi-minimal:8.10`.
### ###
FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5 FROM registry.access.redhat.com/ubi8/ubi-minimal:8.6
WORKDIR /work/ WORKDIR /work/
RUN chown 1001 /work \ RUN chown 1001 /work \
&& chmod "g+rwX" /work \ && chmod "g+rwX" /work \
&& chown 1001:root /work && chown 1001:root /work
COPY --chown=1001:root --chmod=0755 build/*-runner /work/application COPY --chown=1001:root build/*-runner /work/application
EXPOSE 8080 EXPOSE 8080
USER 1001 USER 1001
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
@@ -6,7 +6,7 @@
# #
# Before building the container image run: # Before building the container image run:
# #
# ./gradlew build -Dquarkus.native.enabled=true # ./gradlew build -Dquarkus.package.type=native
# #
# Then, build the image with: # Then, build the image with:
# #
@@ -16,17 +16,15 @@
# #
# docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus # docker run -i --rm -p 8080:8080 quarkus/kontor-quarkus
# #
# The `quay.io/quarkus/ubi9-quarkus-micro-image:2.0` base image is based on UBI 9.
# To use UBI 8, switch to `quay.io/quarkus/quarkus-micro-image:2.0`.
### ###
FROM quay.io/quarkus/ubi9-quarkus-micro-image:2.0 FROM quay.io/quarkus/quarkus-micro-image:1.0
WORKDIR /work/ WORKDIR /work/
RUN chown 1001 /work \ RUN chown 1001 /work \
&& chmod "g+rwX" /work \ && chmod "g+rwX" /work \
&& chown 1001:root /work && chown 1001:root /work
COPY --chown=1001:root --chmod=0755 build/*-runner /work/application COPY --chown=1001:root build/*-runner /work/application
EXPOSE 8080 EXPOSE 8080
USER 1001 USER 1001
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
@@ -0,0 +1,95 @@
package de.thpeetz.kontor.comics;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import de.thpeetz.kontor.comics.service.ArtistService;
import de.thpeetz.kontor.comics.service.ComicsService;
import de.thpeetz.kontor.comics.service.PublisherService;
@Path("/kontor/comics")
public class ComicsResource {
@Inject
transient ComicsService comicsService;
@Inject
transient ArtistService artistService;
@Inject
transient PublisherService publisherService;
@GET
@Produces(MediaType.TEXT_HTML)
@Path("/")
public String showComicList() {
return comicsService.showComicList();
}
@GET
@Produces(MediaType.TEXT_HTML)
@Path("/artist")
public String showArtistList() {
return artistService.showArtistList();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/artist")
public String showArtistListJson() {
return artistService.showArtistList();
}
@GET
@Produces(MediaType.TEXT_HTML)
@Path("/artist/view/{artist_id}")
public String showArtist(String artist_id) {
return artistService.showArtist(artist_id);
}
@GET
@Produces(MediaType.TEXT_HTML)
@Path("/artist/create")
public String showArtistCreation() {
return artistService.showArtistCreation();
}
@POST
@Produces(MediaType.TEXT_HTML)
@Path("/artist/create")
public String validateArtistDetails() {
return artistService.validateArtistDetails();
}
@GET
@Produces(MediaType.TEXT_HTML)
@Path("/publisher")
public String showPublisherList() {
return publisherService.showPublisherList();
}
@GET
@Produces(MediaType.TEXT_HTML)
@Path("/publisher/view/{publisher_id}")
public String showPublisher(String publisher_id) {
return publisherService.showPublisher(publisher_id);
}
@GET
@Produces(MediaType.TEXT_HTML)
@Path("/publisher/create")
public String showPublisherCreation() {
return publisherService.showPublisherCreation();
}
@POST
@Produces(MediaType.TEXT_HTML)
@Path("/publisher/create")
public String validatePublisherDetails() {
return publisherService.validatePublisherDetails();
}
}
@@ -0,0 +1,28 @@
package de.thpeetz.kontor.comics.service;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ArtistService {
public String showArtistList() {
// TODO Auto-generated method stub
return null;
}
public String showArtist(String artist_id) {
// TODO Auto-generated method stub
return null;
}
public String showArtistCreation() {
// TODO Auto-generated method stub
return null;
}
public String validateArtistDetails() {
// TODO Auto-generated method stub
return null;
}
}
@@ -0,0 +1,12 @@
package de.thpeetz.kontor.comics.service;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ComicsService {
public String showComicList() {
return "comics";
}
}
@@ -0,0 +1,28 @@
package de.thpeetz.kontor.comics.service;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class PublisherService {
public String showPublisherList() {
// TODO Auto-generated method stub
return null;
}
public String showPublisher(String publisher_id) {
// TODO Auto-generated method stub
return null;
}
public String showPublisherCreation() {
// TODO Auto-generated method stub
return null;
}
public String validatePublisherDetails() {
// TODO Auto-generated method stub
return null;
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
# Your configuration properties
quarkus.http.test-port=8083
quarkus.http.test-ssl-port=8446
@@ -0,0 +1,8 @@
package de.thpeetz.kontor.comics;
import io.quarkus.test.junit.QuarkusIntegrationTest;
@QuarkusIntegrationTest
public class ComicsResourceIT extends ComicsResourceTest {
}
@@ -0,0 +1,19 @@
package de.thpeetz.kontor.comics;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
import io.quarkus.test.junit.QuarkusTest;
@QuarkusTest
class ComicsResourceTest {
@Test
void testComicsEndpoint() {
given().when().get("/kontor/comics")
.then()
.statusCode(200)
.body(is("comics"));
}
}
-3
View File
@@ -1,3 +0,0 @@
dist
node_modules
-42
View File
@@ -1,42 +0,0 @@
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db
-15
View File
@@ -1,15 +0,0 @@
### STAGE 1: Build ###
FROM docker.io/node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
### STAGE 2: Run ###
FROM docker.io/library/nginx:stable-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist/kontor-angular/browser /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
-8
View File
@@ -1,8 +0,0 @@
.PHONY: docker dev
docker:
docker build -t kontor-angular:0.2.0-SNAPSHOT .
dev:
ng serve
-59
View File
@@ -1,59 +0,0 @@
# KontorAngular
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.2.1.
## Development server
To start a local development server, run:
```bash
ng serve
```
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
## Code scaffolding
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
```bash
ng generate component component-name
```
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
```bash
ng generate --help
```
## Building
To build the project run:
```bash
ng build
```
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
## Running unit tests
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
```bash
ng test
```
## Running end-to-end tests
For end-to-end (e2e) testing, run:
```bash
ng e2e
```
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
## Additional Resources
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
-95
View File
@@ -1,95 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"kontor-angular": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css"
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"buildTarget": "kontor-angular:build:production"
},
"development": {
"buildTarget": "kontor-angular:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular/build:extract-i18n"
},
"test": {
"builder": "@angular/build:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css"
]
}
}
}
}
},
"cli": {
"analytics": false
}
}
-38
View File
@@ -1,38 +0,0 @@
server {
listen 8800;
# Root-Verzeichnis für den Server setzen (wir kopieren unsere Anwendung hierher)
root /usr/share/nginx/html;
# Definieren der Standard-Indexdatei (Angular erstellt die Datei index.html für uns und sie befindet sich im oben genannten Verzeichnis)
index index.html;
# Cache-Header für Medien-ASsets
location ~* \.(?:cur|jpe?g|gif|htc|ico|png|xml|otf|ttf|eot|woff|woff2|svg)$ {
access_log off;
add_header Pragma "must-revalidate, public";
add_header Cache-Control "must-revalidate, public";
expires max;
tcp_nodelay off;
}
# Cache-Header für HTML, CSS und JS-Dateien
location ~* \.(?:css|js|html)$ {
access_log off;
add_header Pragma "must-revalidate, public";
add_header Cache-Control "must-revalidate, public";
expires 2d;
tcp_nodelay off;
}
# Konfiguration für den /-Pfad
location / {
# Zunächst versuchen wir die angeforderte URI auzuliefern
# Klappt das nicht, versuchen wir es mit einem abschließenden Slash
# Klappt auch das nicht, liefern wir die index.html aus.
# Das ist nötig, damit Angular-Routen korrekt augeflöst und ausgeliefert werden
try_files $uri $uri/ /index.html;
}
}
-9970
View File
File diff suppressed because it is too large Load Diff
-48
View File
@@ -1,48 +0,0 @@
{
"name": "kontor-angular",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"prettier": {
"printWidth": 100,
"singleQuote": true,
"overrides": [
{
"files": "*.html",
"options": {
"parser": "angular"
}
}
]
},
"private": true,
"dependencies": {
"@angular/common": "^20.2.0",
"@angular/compiler": "^20.2.0",
"@angular/core": "^20.2.0",
"@angular/forms": "^20.2.0",
"@angular/platform-browser": "^20.2.0",
"@angular/router": "^20.2.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular/build": "^20.2.1",
"@angular/cli": "^20.2.1",
"@angular/compiler-cli": "^20.2.0",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.9.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.9.2"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 634 B

-9
View File
@@ -1,9 +0,0 @@
h1 {
color: blue;
}
.app {
font-family: Arial, Helvetica, sans-serif;
/* max-width: 500px; */
margin: auto;
}
-10
View File
@@ -1,10 +0,0 @@
<div class="app">
<kontor-header />
<app-navigation/>
<main>
<router-outlet />
</main>
<kontor-footer />
</div>
-14
View File
@@ -1,14 +0,0 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { KontorHeaderComponent } from "./kontor/header/header.component";
import { KontorFooterComponent } from './kontor/footer/footer.component';
import { NavigationComponent } from "./kontor/navigation/navigation.component";
@Component({
selector: 'app-root',
imports: [RouterOutlet, KontorHeaderComponent, KontorFooterComponent, NavigationComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
}
-13
View File
@@ -1,13 +0,0 @@
import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes, withComponentInputBinding()),
provideHttpClient(),
]
};
-26
View File
@@ -1,26 +0,0 @@
import { Routes } from '@angular/router';
import { KontorComponent } from './kontor/kontor.component';
import { Auth } from './common/auth/auth';
import { ComicSectionComponent } from './kontor/comic/comic-section/comic-section.component';
import { comicRoutes } from './kontor/comic/comic-section/comic-section.routes';
import { TyscSectionComponent } from './kontor/tysc/tysc-section/tysc-section.component';
import { tyscRoutes } from './kontor/tysc/tysc-section/tysc-section.routes';
import { MediaSectionComponent } from './kontor/media/media-section/media-section.component';
import { mediaRoutes } from './kontor/media/media-section/media-section.routes';
export const routes: Routes = [
{ path: '', component: KontorComponent, },
{ path: 'login', component: Auth, },
{
path: 'comic', component: ComicSectionComponent,
children: comicRoutes,
},
{
path: 'tysc', component: TyscSectionComponent,
children: tyscRoutes,
},
{
path: 'media', component: MediaSectionComponent,
children: mediaRoutes,
},
];
-23
View File
@@ -1,23 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { App } from './app';
describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [App],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(App);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it('should render title', () => {
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, kontor-angular');
});
});
@@ -1,16 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { AuthService } from './auth-service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -1,55 +0,0 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
import { catchError, Observable, throwError } from 'rxjs';
interface AuthResponseData {
kind: string;
idToken: string;
email: string;
refreshToken: string;
expiresIn: string;
}
export interface TokenData {
access_token: string;
token_type: string;
}
@Injectable({providedIn: 'root'})
export class AuthService {
private http = inject(HttpClient);
signup(email: string, password: string) {
return this.http.post<TokenData>('http://localhost:8800/signup',
{
username: email,
password: password
}
).pipe(catchError(errorRes => {
let errorMessage = 'An unknown error occurred!';
const err = Error(errorMessage);
if (!errorRes.error) {
return throwError(() => err);
}
switch(errorRes.statusText) {
case 'ERROR':
errorMessage = 'Uups...';
}
return throwError(() => Error(errorMessage));
}));
}
login(email: string, password: string): Observable<TokenData> {
const params = new HttpParams()
.set('username', email)
.append('password', password);
return this.http.post<TokenData>('http://127.0.0.1:8800/api/login/token', params,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
}
);
}
}
@@ -1,8 +0,0 @@
button {
text-align: center;
padding: 10px;
border-radius: 10px;
margin-left: 14px;
}
@@ -1,34 +0,0 @@
<div class="row">
<div style="margin-top: 16px;">
@if(error) {
<div class="alert alter-danger">
<p>{{ error }}</p>
</div>
}
@if(isLoading) {
<div style="text-align: center;">
<app-loading-spinner></app-loading-spinner>
</div>
}
@else {
<form (ngSubmit)="onSubmit(authForm)" #authForm="ngForm">
<div class="form-group" style="margin-bottom: 7px;">
<label for="email">E-Mail</label>
<input type="email" id="email" class="form-control" ngModel name="email" required email=""/>
</div>
<div class="form-group" style="margin-bottom: 7px;">
<label for="password">Password</label>
<input type="password" id="password" class="form-control" ngModel name="password" required/>
</div>
<div>
<button class="btn btn-primary" type="submit" [disabled]="!authForm.valid">
{{ isLoginMode ? 'Login' : 'Sign Up' }}
</button>
<button class="btn btn-primary" (click)="onSwitchMode()" type="button">
Switch to {{ isLoginMode ? 'Sign Up' : 'Login' }}
</button>
</div>
</form>
}
</div>
</div>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Auth } from './auth';
describe('Auth', () => {
let component: Auth;
let fixture: ComponentFixture<Auth>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [Auth]
})
.compileComponents();
fixture = TestBed.createComponent(Auth);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,55 +0,0 @@
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { NgForm } from '@angular/forms';
import { AuthService, TokenData } from './auth-service';
import { LoadingSpinner } from "../../shared/loading-spinner/loading-spinner";
import { Observable } from 'rxjs';
import { HttpErrorResponse } from '@angular/common/http';
@Component({
selector: 'app-auth',
imports: [FormsModule, LoadingSpinner],
templateUrl: './auth.html',
styleUrl: './auth.css'
})
export class Auth {
isLoginMode = true;
isLoading = false;
error: string | null = null;
constructor(private authService: AuthService) {}
onSwitchMode() {
this.isLoginMode =!this.isLoginMode;
}
onSubmit(form: NgForm) {
if (!form.valid) {
return;
}
const email = form.value.email;
const password = form.value.password;
let authObservable: Observable<TokenData>;
this.isLoading = true;
if (this.isLoginMode) {
authObservable = this.authService.login(email, password);
} else {
authObservable = this.authService.signup(email, password)
}
authObservable.subscribe({
next: token => {
console.log(token);
localStorage.setItem('userToken', JSON.stringify(token));
this.isLoading = false;
},
error: err => {
console.log(err);
this.isLoading = false;
}
});
form.reset();
}
}
@@ -1,5 +0,0 @@
div {
border-radius: 6px;
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
@@ -1,5 +0,0 @@
<div>
<a [routerLink]="['/', 'comic', 'artist', artist().id]" routerLinkActive="active">
<span>{{ artist().name }}</span>
</a>
</div>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComicArtistComponent } from './comic-artist.component';
describe('ComicArtistComponent', () => {
let component: ComicArtistComponent;
let fixture: ComponentFixture<ComicArtistComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComicArtistComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ComicArtistComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,13 +0,0 @@
import { Component, input } from '@angular/core';
import { Artist } from '../comic.model';
import { RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-comic-artist',
imports: [RouterLink, RouterLinkActive],
templateUrl: './comic-artist.component.html',
styleUrl: './comic-artist.component.css'
})
export class ComicArtistComponent {
artist = input.required<Artist>();
}
@@ -1,14 +0,0 @@
ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
gap: 0.5rem;
overflow: auto;
}
@media (min-width: 768px) {
ul {
flex-direction: column;
}
}
@@ -1,7 +0,0 @@
<ul>
@for (artist of artists(); track artist.id) {
<li>
<app-comic-artist [artist]="artist"/>
</li>
}
</ul>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComicArtistsListComponent } from './comic-artists-list.component';
describe('ComicArtistsListComponent', () => {
let component: ComicArtistsListComponent;
let fixture: ComponentFixture<ComicArtistsListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComicArtistsListComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ComicArtistsListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,38 +0,0 @@
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
import { Artist } from '../comic.model';
import { ComicArtistComponent } from '../comic-artist/comic-artist.component';
import { ArtistService } from '../comic-artists/artist.service';
@Component({
selector: 'app-comic-artists-list',
imports: [ComicArtistComponent],
templateUrl: './comic-artists-list.component.html',
styleUrl: './comic-artists-list.component.css'
})
export class ComicArtistsListComponent implements OnInit {
artists = signal<Artist[]>([]);
isFetching = signal(false);
error = signal('');
private artistService = inject(ArtistService);
private destroyRef = inject(DestroyRef);
ngOnInit() {
this.isFetching.set(true);
const subscription = this.artistService.loadArtists().subscribe({
next: (artists) => {
this.artists.set(artists);
},
error: (error: Error) => {
this.error.set(error.message);
},
complete: () => {
this.isFetching.set(false);
},
});
this.destroyRef.onDestroy(() => {
subscription.unsubscribe();
});
}
}
@@ -1,47 +0,0 @@
import { inject, Injectable, signal } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { catchError, map, throwError } from "rxjs";
import { ErrorService } from "../../../shared/error.service";
import { Artist, ArtistDetails } from "../comic.model";
@Injectable({
providedIn: 'root',
})
export class ArtistService {
private errorService = inject(ErrorService);
private httpClient = inject(HttpClient);
private artists = signal<Artist[]>([]);
loadedArtists = this.artists.asReadonly();
loadArtists() {
return this.fetchArtists('http://127.0.0.1:8800/api/comics/artists', 'Someting went wrong fetching artists. Please try again later-');
}
loadArtistDetails(artistId: string | null) {
return this.fetchArtistDetails('http://127.0.0.1:8800/api/comics/artists/' + artistId, 'Someting went wrong fetching comic artists. Please try again later.');
}
private fetchArtists(url: string, errorMessage: string) {
return this.httpClient.get<Artist[]>(url).pipe(
map((resData) => resData),
catchError((error) => {
console.log(error);
return throwError(() => new Error(errorMessage));
})
);
}
private fetchArtistDetails(url: string, errorMessage: string) {
return this.httpClient.get<ArtistDetails>(url).pipe(
map((resData) => {
console.log(resData);
return resData;
}),
catchError((error) => {
console.log(error);
return throwError(() => new Error(errorMessage));
})
);
}
}
@@ -1,16 +0,0 @@
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 20px;
}
section {
margin-left: 10px;
padding: 10px;
background-color: darkgrey;
border-radius: 10px;
margin-bottom: 10px;
}
article {
margin-left: 10px;
padding: 5px;
}
@@ -1,35 +0,0 @@
<div class="grid-container">
<div>
<app-comic-artists-list />
</div>
<div>
@if (artist()) {
<section>
<h2>{{ artist().name }}</h2>
<a href="{{ artist().weblink }}" style="background-color: green;">{{ artist().name }}</a>
</section>
<section>
@for (work of artist().comic_works; track work.worktype.id) {
<app-comic-worktype [worktype]="work.worktype"/>
@for (comic of work.comics; track comic.id) {
<article>
<app-comic-comic [comic]="comic"/>
</article>
}
}
</section>
<section>
@for (work of artist().issue_works; track work.worktype.id) {
<app-comic-worktype [worktype]="work.worktype"/>
@for (issue of work.issues; track issue.id) {
<article>
<app-comic-comic [comic]="issue.comic"/>
</article>
}
}
</section>
} @else {
<h2>Artist Details</h2>
}
</div>
</div>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComicArtistsComponent } from './comic-artists.component';
describe('ComicArtistsComponent', () => {
let component: ComicArtistsComponent;
let fixture: ComponentFixture<ComicArtistsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComicArtistsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ComicArtistsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,25 +0,0 @@
import { Component, inject, input } from '@angular/core';
import { ComicArtistsListComponent } from '../comic-artists-list/comic-artists-list.component';
import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router';
import { ArtistService } from './artist.service';
import { ComicComicComponent } from "../comic-comic/comic-comic.component";
import { ComicWorktypeComponent } from "../comic-worktype/comic-worktype.component";
import { ArtistDetails } from '../comic.model';
@Component({
selector: 'app-comic-artists',
imports: [ComicArtistsListComponent, ComicComicComponent, ComicWorktypeComponent],
templateUrl: './comic-artists.component.html',
styleUrl: './comic-artists.component.css'
})
export class ComicArtistsComponent {
artist = input.required<ArtistDetails>();
}
export const artistResolver: ResolveFn<ArtistDetails> = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
const artistService = inject(ArtistService);
const artistId = route.paramMap.get('artistId');
const artistDetails = artistService.loadArtistDetails(artistId);
console.log(artistDetails);
return artistDetails;
};
@@ -1,5 +0,0 @@
<div>
<a [routerLink]="['/', 'comic', 'comics', comic().id]" routerLinkActive="active">
<span>{{ comic().title }}</span>
</a>
</div>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComicComicComponent } from './comic-comic.component';
describe('ComicComicComponent', () => {
let component: ComicComicComponent;
let fixture: ComponentFixture<ComicComicComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComicComicComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ComicComicComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,13 +0,0 @@
import { Component, input } from '@angular/core';
import { Comic } from '../comic.model';
import { RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-comic-comic',
imports: [RouterLink, RouterLinkActive],
templateUrl: './comic-comic.component.html',
styleUrl: './comic-comic.component.css'
})
export class ComicComicComponent {
comic = input.required<Comic>();
}
@@ -1,14 +0,0 @@
ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
gap: 0.5rem;
overflow: auto;
}
@media (min-width: 768px) {
ul {
flex-direction: column;
}
}
@@ -1,10 +0,0 @@
<div>
<input #searchQuery type="text" (input)="search($event.target.value)" placeholder="Search" />
</div>
<ul>
@for (comic of comics(); track comic.id) {
<li>
<app-comic-comic [comic]="comic"/>
</li>
}
</ul>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComicComicsListComponent } from './comic-comics-list.component';
describe('ComicComicsListComponent', () => {
let component: ComicComicsListComponent;
let fixture: ComponentFixture<ComicComicsListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComicComicsListComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ComicComicsListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,46 +0,0 @@
import { Component, computed, DestroyRef, inject, OnInit, signal } from '@angular/core';
import { Comic } from '../comic.model';
import { ComicService } from '../comic-comics/comic.service';
import { ComicComicComponent } from '../comic-comic/comic-comic.component';
@Component({
selector: 'app-comic-comics-list',
imports: [ComicComicComponent],
templateUrl: './comic-comics-list.component.html',
styleUrl: './comic-comics-list.component.css',
})
export class ComicComicsListComponent implements OnInit {
comicsFetched = signal<Comic[] | undefined>(undefined);
searchQuery = signal<string>('');
comics = computed(() => {
const sq = this.searchQuery();
return this.comicsFetched()?.filter(c => c.title.toLowerCase().includes(sq.toLowerCase()));
});
isFetching = signal(false);
error = signal('');
private comicsService = inject(ComicService);
private destroyRef = inject(DestroyRef);
ngOnInit() {
this.isFetching.set(true);
const subscription = this.comicsService.loadComics().subscribe({
next: (comics) => {
this.comicsFetched.set(comics);
},
error: (error: Error) => {
this.error.set(error.message);
},
complete: () => {
this.isFetching.set(false);
},
});
this.destroyRef.onDestroy(() => {
subscription.unsubscribe();
});
}
search(query: string) {
this.searchQuery.set(query);
}
}
@@ -1,29 +0,0 @@
.float-parent-element {
width: 50%;
}
.float-child-element {
float: left;
width: 50%;
border-width: 10px;
border-color: black;
}
.float-details-element {
border: 1px solid darkgreen;
background-color: lightgreen;
margin: 5px;
padding: 1rem;
height: 100%;
}
.parent {
border: 1px solid black;
margin: 1rem;
padding: 2rem 2rem;
text-align: left;
}
.child {
display: inline-block;
border: 1px solid red;
padding: 1rem 1rem;
vertical-align: top;
}
@@ -1,32 +0,0 @@
<div class="grid-container">
<div>
<app-comic-comics-list/>
</div>
<div>
@if (comic()) {
<section>
<h2>{{ comic().title }}</h2>
<a href="{{ comic().weblink }}" style="background-color: green;">{{ comic().title }}</a>
<app-comic-publisher [publisher]="comic().publisher" />
</section>
<section>
@for (issue of comic().issues; track issue.id) {
<app-comic-issue [issue]="issue"/>
}
</section>
<section>
@for (work of comic().works; track work.worktype.id) {
<app-comic-worktype [worktype]="work.worktype"/>
@for (artist of work.artists; track artist.id) {
<article>
<app-comic-artist [artist]="artist"/>
</article>
}
}
</section>
} @else {
<h2>Comic Details</h2>
}
</div>
</div>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComicComicsComponent } from './comic-comics.component';
describe('ComicComicsComponent', () => {
let component: ComicComicsComponent;
let fixture: ComponentFixture<ComicComicsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComicComicsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ComicComicsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,27 +0,0 @@
import { Component, inject, input } from '@angular/core';
import { ComicComicsListComponent } from "../comic-comics-list/comic-comics-list.component";
import { ComicDetails } from '../comic.model';
import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router';
import { ComicService } from './comic.service';
import { ComicWorktypeComponent } from '../comic-worktype/comic-worktype.component';
import { ComicArtistComponent } from '../comic-artist/comic-artist.component';
import { ComicIssueComponent } from '../comic-issue/comic-issue.component';
import { ComicPublisherComponent } from "../comic-publisher/comic-publisher.component";
@Component({
selector: 'app-comic-comics',
imports: [ComicComicsListComponent, ComicWorktypeComponent, ComicArtistComponent, ComicIssueComponent, ComicPublisherComponent],
templateUrl: './comic-comics.component.html',
styleUrl: './comic-comics.component.css'
})
export class ComicComicsComponent {
comic = input.required<ComicDetails>();
}
export const comicResolver: ResolveFn<ComicDetails> = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
const comicService = inject(ComicService);
const comicId = route.paramMap.get('comicId');
const comicDetails = comicService.loadComicDetails(comicId);
console.log(comicDetails);
return comicDetails;
};
@@ -1,45 +0,0 @@
import { HttpClient } from "@angular/common/http";
import { inject, Injectable, signal } from "@angular/core";
import { Comic, ComicDetails } from "../comic.model";
import { catchError, map, throwError } from "rxjs";
@Injectable({
providedIn: 'root'
})
export class ComicService {
private httpClient = inject(HttpClient);
private comics = signal<Comic[]>([]);
loadedComics = this.comics.asReadonly();
loadComics() {
return this.fetchComics('http://127.0.0.1:8800/api/comics/comics', 'Someting went wrong fetching comics. Please try again later.');
}
loadComicDetails(comicId: string | null) {
return this.fetchComicDetails('http://127.0.0.1:8800/api/comics/comics/' + comicId, 'Someting went wrong fetching comics. Please try again later.');
}
private fetchComicDetails(url: string, errorMessage: string) {
return this.httpClient.get<ComicDetails>(url).pipe(
map((resData) => {
console.log(resData);
return resData;
}),
catchError((error) => {
console.log(error);
return throwError(() => new Error(errorMessage));
})
);
}
private fetchComics(url: string, errorMessage: string) {
return this.httpClient.get<Comic[]>(url).pipe(
map((resData) => resData),
catchError((error) => {
console.log(error);
return throwError(() => new Error(errorMessage));
})
);
}
}
@@ -1,5 +0,0 @@
<div>
<a [routerLink]="['/', 'comic', 'issue', issue().id]" routerLinkActive="active">
<span>{{ issue().issue_number }}</span>
</a>
</div>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComicIssueComponent } from './comic-issue.component';
describe('ComicIssueComponent', () => {
let component: ComicIssueComponent;
let fixture: ComponentFixture<ComicIssueComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComicIssueComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ComicIssueComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,13 +0,0 @@
import { Component, input } from '@angular/core';
import { Issue } from '../comic.model';
import { RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-comic-issue',
imports: [RouterLink, RouterLinkActive],
templateUrl: './comic-issue.component.html',
styleUrl: './comic-issue.component.css'
})
export class ComicIssueComponent {
issue = input.required<Issue>();
}
@@ -1,5 +0,0 @@
<div class="subnav">
<a routerLink="/comic/comics" routerLinkActive="active">Comics</a>
<a routerLink="/comic/publisher" routerLinkActive="active">Publisher</a>
<a routerLink="/comic/artist" routerLinkActive="active">Artists</a>
</div>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComicNavigationComponent } from './comic-navigation.component';
describe('ComicNavigationComponent', () => {
let component: ComicNavigationComponent;
let fixture: ComponentFixture<ComicNavigationComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComicNavigationComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ComicNavigationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,12 +0,0 @@
import { Component } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-comic-navigation',
imports: [RouterLink, RouterLinkActive],
templateUrl: './comic-navigation.component.html',
styleUrl: './comic-navigation.component.css'
})
export class ComicNavigationComponent {
}
@@ -1,5 +0,0 @@
<div>
<a [routerLink]="['/', 'comic', 'publisher', publisher().id]" routerLinkActive="active">
<span>{{ publisher().name }}</span>
</a>
</div>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComicPublisherComponent } from './comic-publisher.component';
describe('ComicPublisherComponent', () => {
let component: ComicPublisherComponent;
let fixture: ComponentFixture<ComicPublisherComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComicPublisherComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ComicPublisherComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,13 +0,0 @@
import { Component, input } from '@angular/core';
import { Publisher } from '../comic.model';
import { RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-comic-publisher',
imports: [RouterLink, RouterLinkActive],
templateUrl: './comic-publisher.component.html',
styleUrl: './comic-publisher.component.css'
})
export class ComicPublisherComponent {
publisher = input.required<Publisher>();
}
@@ -1,14 +0,0 @@
ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
gap: 0.5rem;
overflow: auto;
}
@media (min-width: 768px) {
ul {
flex-direction: column;
}
}
@@ -1,7 +0,0 @@
<ul>
@for (publisher of publishers(); track publisher.id) {
<li>
<app-comic-publisher [publisher]="publisher" />
</li>
}
</ul>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComicPublishersListComponent } from './comic-publishers-list.component';
describe('ComicPublishersListComponent', () => {
let component: ComicPublishersListComponent;
let fixture: ComponentFixture<ComicPublishersListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComicPublishersListComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ComicPublishersListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,37 +0,0 @@
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
import { Publisher } from '../comic.model';
import { PublisherService } from '../comic-publishers/publisher.service';
import { ComicPublisherComponent } from "../comic-publisher/comic-publisher.component";
@Component({
selector: 'app-comic-publishers-list',
imports: [ComicPublisherComponent],
templateUrl: './comic-publishers-list.component.html',
styleUrl: './comic-publishers-list.component.css'
})
export class ComicPublishersListComponent implements OnInit {
publishers = signal<Publisher[]>([]);
isFetching = signal(false);
error = signal('');
private publisherService = inject(PublisherService);
private destroyRef = inject(DestroyRef);
ngOnInit() {
this.isFetching.set(true);
const subscription = this.publisherService.loadPublishers().subscribe({
next: (publishers) => {
this.publishers.set(publishers);
},
error: (error: Error) => {
this.error.set(error.message);
},
complete: () => {
this.isFetching.set(false);
},
});
this.destroyRef.onDestroy(() => {
subscription.unsubscribe();
});
}
}
@@ -1,31 +0,0 @@
<div class="grid-container">
<div>
<app-comic-publishers-list />
</div>
<div>
@if (publisher()) {
<section>
<h2>{{ publisher().name }}</h2>
@if (publisher().parent_publisher) {
<app-comic-publisher [publisher]="publisher().parent_publisher" />
}
</section>
<section>
@for (imprint of publisher().imprints; track imprint.id) {
<article>
<app-comic-publisher [publisher]="imprint" />
</article>
}
</section>
<section>
@for (comic of publisher().comics; track comic.id) {
<article>
<app-comic-comic [comic]="comic" />
</article>
}
</section>
} @else {
<h2>Publisher Details</h2>
}
</div>
</div>
@@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComicPublishersComponent } from './comic-publishers.component';
describe('ComicPublishersComponent', () => {
let component: ComicPublishersComponent;
let fixture: ComponentFixture<ComicPublishersComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ComicPublishersComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ComicPublishersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -1,25 +0,0 @@
import { Component, inject, input } from '@angular/core';
import { Publisher, PublisherDetails } from '../comic.model';
import { ComicPublishersListComponent } from '../comic-publishers-list/comic-publishers-list.component';
import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router';
import { PublisherService } from './publisher.service';
import { ComicPublisherComponent } from "../comic-publisher/comic-publisher.component";
import { ComicComicComponent } from "../comic-comic/comic-comic.component";
@Component({
selector: 'app-comic-publishers',
imports: [ComicPublishersListComponent, ComicPublisherComponent, ComicComicComponent],
templateUrl: './comic-publishers.component.html',
styleUrl: './comic-publishers.component.css'
})
export class ComicPublishersComponent {
publisher = input.required<PublisherDetails>();
}
export const publisherResolver: ResolveFn<PublisherDetails> = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
const publisherService = inject(PublisherService);
const publisherId = route.paramMap.get('publisherId');
const publisherDetails = publisherService.loadPublisherDetails(publisherId);
console.log(publisherDetails);
return publisherDetails;
};

Some files were not shown because too many files have changed in this diff Show More