19 Commits

Author SHA1 Message Date
Thomas Peetz e2ff26f6bf Setup MessageModerator as Docker build
refs #28
2025-07-16 16:04:33 +02:00
Thomas Peetz 0d99f383fb add MediaFile from queue 2025-07-11 16:49:42 +02:00
Thomas Peetz c6fd80408b read queue 2025-07-10 22:04:31 +02:00
Thomas Peetz d2d4deb350 set messages durable 2025-07-10 15:58:11 +02:00
tpeetz ee280e0b8e Merge branch 'feature/27-use-amqp-protocol-for-messages' into 'develop/0.2.0'
use AMQP protocol for messages

Closes #27

See merge request tpeetz/kontor!30
2025-07-07 15:52:04 +02:00
Thomas Peetz 1c2c2f38a4 use message for adding links 2025-07-07 15:49:33 +02:00
Thomas Peetz b5cca50960 add_link.py sends message 2025-07-06 22:46:47 +02:00
tpeetz 0a505fabcf Merge branch 'feature/25-integrate-apache-camel' into 'develop/0.2.0'
integrate apache camel

Closes #25 and #26

See merge request tpeetz/kontor!29
2025-07-04 00:29:48 +02:00
Thomas Peetz 4b8053c6e2 fix configuration to connect to Artemis from Sprint Boot for all environments 2025-07-04 00:29:15 +02:00
Thomas Peetz 5df3c1c47e integrate Apache Camel into Spring Boot 2025-07-03 17:42:30 +02:00
Thomas Peetz adad4f88da configure actuator endpoints 2025-07-03 11:31:40 +02:00
Thomas Peetz 957c7a702e Extend docker-compose.yml network configuration for camel-karavan 2025-07-03 07:33:24 +02:00
tpeetz b5344a9ed1 Merge branch 'feature/24-add-docker-image-for-apache-camel-caravan' into 'develop/0.2.0'
Add Docker image for Apache Camel Caravan

Closes #24

See merge request tpeetz/kontor!28
2025-07-01 22:28:37 +02:00
Thomas Peetz 16ad701eed Extend docker-compose.yml for camel-karavan, registry and gitea 2025-07-01 22:23:24 +02:00
Thomas Peetz 2ba7465675 change port and credentials for messages 2025-07-01 07:01:34 +02:00
tpeetz c703dfc6e3 Merge branch 'feature/23-add-docker-image-for-apache-activemq' into 'develop/0.2.0'
Add Docker image for Apache ActiveMQ

Closes #23

See merge request tpeetz/kontor!26
2025-06-30 17:39:10 +02:00
Thomas Peetz b62cc89f71 add Apache ActiveMQ Artemis as Docker image to docker-compose.yml 2025-06-30 17:33:39 +02:00
tpeetz e741a46f69 Merge branch 'feature/22-add-docker-image-for-couchdb' into 'develop/0.2.0'
Add Docker image for CouchDB

Closes #22

See merge request tpeetz/kontor!25
2025-06-30 16:42:08 +02:00
Thomas Peetz 4f87ff83ce add CouchDB as Docker image to docker-compose.yml 2025-06-30 16:38:15 +02:00
619 changed files with 4768 additions and 38442 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 }}."
+3 -2
View File
@@ -1,6 +1,6 @@
.vscode/
.idea/ .idea/
.angular/ .theia/
.vscode/
__pycache__/ __pycache__/
bonus/ bonus/
icons/ icons/
@@ -18,3 +18,4 @@ db-password.txt
kontor-api/tests/test_main.py kontor-api/tests/test_main.py
kontor-api/tests/test_db.db kontor-api/tests/test_db.db
kontor-api/test_db.db kontor-api/test_db.db
couchdb-password.txt
-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
+33 -75
View File
@@ -16,8 +16,7 @@ services:
ports: ports:
- 5432:5432 - 5432:5432
networks: networks:
- integration - database
- frontend
volumes: volumes:
- postgres-data:/var/lib/postgresql/data:rw - postgres-data:/var/lib/postgresql/data:rw
secrets: secrets:
@@ -29,6 +28,33 @@ services:
networks: networks:
- database - database
- frontend - frontend
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
activemq:
image: apache/activemq-artemis:latest-alpine
restart: unless-stopped
ports:
- 61616:61616
- 8161:8161
- 5672:5672
networks:
- integration
- frontend
volumes:
- activemq-data:/var/lib/artemis-instance
kontor: kontor:
image: kontor:0.2.0-SNAPSHOT image: kontor:0.2.0-SNAPSHOT
restart: unless-stopped restart: unless-stopped
@@ -46,79 +72,6 @@ services:
kontor-api: kontor-api:
image: kontor-api:0.2.0-SNAPSHOT image: kontor-api:0.2.0-SNAPSHOT
restart: unless-stopped restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://kontor-api:8500/health"]
interval: 10s
timeout: 5s
retries: 3
networks:
- 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: networks:
- database - database
- integration - integration
@@ -131,13 +84,18 @@ services:
postgres: postgres:
condition: service_healthy condition: service_healthy
networks: networks:
database:
integration: integration:
name: integration name: integration
frontend: frontend:
volumes: volumes:
postgres-data: postgres-data:
couchdb-data:
activemq-data:
images-data: images-data:
secrets: secrets:
db-password: db-password:
file: db-password.txt file: db-password.txt
couchdb-password:
file: couchdb-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;
};
@@ -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 { Publisher, PublisherDetails } from "../comic.model";
@Injectable({
providedIn: 'root',
})
export class PublisherService {
private errorService = inject(ErrorService);
private httpClient = inject(HttpClient);
private publishers = signal<Publisher[]>([]);
loadedPublishers = this.publishers.asReadonly();
loadPublishers() {
return this.fetchPublishers('http://127.0.0.1:8800/api/comics/publishers', 'Someting went wrong fetching artists. Please try again later-');
}
loadPublisherDetails(artistId: string | null) {
return this.fetchPublisherDetails('http://127.0.0.1:8800/api/comics/publishers/' + artistId, 'Someting went wrong fetching comic artists. Please try again later.');
}
private fetchPublishers(url: string, errorMessage: string) {
return this.httpClient.get<Publisher[]>(url).pipe(
map((resData) => resData),
catchError((error) => {
console.log(error);
return throwError(() => new Error(errorMessage));
})
);
}
private fetchPublisherDetails(url: string, errorMessage: string) {
return this.httpClient.get<PublisherDetails>(url).pipe(
map((resData) => {
console.log(resData);
return resData;
}),
catchError((error) => {
console.log(error);
return throwError(() => new Error(errorMessage));
})
);
}
}
@@ -1,2 +0,0 @@
<app-comic-navigation />
<router-outlet></router-outlet>

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