From 77532dfe48a2cca566f0a7cdcbcddd782a1166b4 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 15 Oct 2024 08:53:38 +0200 Subject: [PATCH 01/30] add auto data-migration --- docker/dev-with-prod/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index db47c48efa..74e1f4e7d5 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -10,7 +10,7 @@ services: environment: SPRING_PROFILES_ACTIVE: staging LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: debug - SPRING_FLYWAY_LOCATION: classpath:db/migration,classpath:db/data-migration,classpath:db/callback + SPRING_FLYWAY_LOCATIONS: classpath:db/migration,classpath:db/data-migration,classpath:db/callback volumes: - ../../../okr/backend/target:/app-root/backend network_mode: "host" From 98f6295614fb8c2bdab94375da7f14475416d0e0 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 15 Oct 2024 10:50:14 +0200 Subject: [PATCH 02/30] jar is now debuggable --- docker/dev-with-prod/docker-compose.yml | 12 ++++++------ docker/local-prod/local-prod.Dockerfile | 12 ++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 docker/local-prod/local-prod.Dockerfile diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index 74e1f4e7d5..2cf16cb0a4 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -2,6 +2,7 @@ include: - ../docker-compose.yml services: spring: + tty: true container_name: spring build: context: . @@ -16,9 +17,10 @@ services: network_mode: "host" maven: + tty: true container_name: maven image: maven:3.9.9-amazoncorretto-21 - command: sh -c "mvn fizzed-watcher:run" + command: sh -c "mvn -B clean package -P build-for-docker && mvn fizzed-watcher:run" working_dir: /app-root/ volumes: - ../../../okr:/app-root/ @@ -26,10 +28,8 @@ services: angular: container_name: angular - image: node:22 - user: "${UID:-1000}:${GID:-1000}" + image: node:20 + tty: true volumes: - ../../../okr:/opt - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - command: [ "/bin/bash", "-c", "cd /opt/frontend && npm ci && npm run watch:prod" ] + command: [ "/bin/bash", "-c", "cd /opt/frontend && mkdir -p dist && npm ci && npm run watch:prod" ] diff --git a/docker/local-prod/local-prod.Dockerfile b/docker/local-prod/local-prod.Dockerfile new file mode 100644 index 0000000000..c224fc3c76 --- /dev/null +++ b/docker/local-prod/local-prod.Dockerfile @@ -0,0 +1,12 @@ +FROM alpine:3.20 + +USER root + +RUN apk update && apk add --upgrade curl && apk --no-cache add openjdk17 inotify-tools + +RUN adduser --home /app-root --uid 1001 --disabled-password okr +USER 1001 + +WORKDIR app-root/backend + +ENTRYPOINT ["/bin/sh", "-c", "export BACKEND_VERSION=$(find . -type f -name 'backend-*.jar' -print -quit | sed -n 's/.*backend-\\(.*\\)\\.jar/\\1/p'); while true; do java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005 -jar backend-${BACKEND_VERSION}.jar & pid=$!; inotifywait -e modify backend-${BACKEND_VERSION}.jar; kill -9 $pid; echo 'JAR updated. Restarting...'; done"] \ No newline at end of file From 55a164a2a70db29b5651ccc680ad0803da8136bc Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 15 Oct 2024 10:57:07 +0200 Subject: [PATCH 03/30] add jar debug dev tools only on profile --- docker/dev-with-prod/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index 2cf16cb0a4..f51e73d08f 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -20,7 +20,7 @@ services: tty: true container_name: maven image: maven:3.9.9-amazoncorretto-21 - command: sh -c "mvn -B clean package -P build-for-docker && mvn fizzed-watcher:run" + command: sh -c "mvn -B clean package -P build-for-docker,debug && mvn fizzed-watcher:run" working_dir: /app-root/ volumes: - ../../../okr:/app-root/ From 3d430e80d1969e3e6891308554e318d62085bb49 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 15 Oct 2024 11:29:16 +0200 Subject: [PATCH 04/30] change log levels --- .../src/main/resources/application-staging.properties | 10 +++++++--- docker/dev-with-prod/docker-compose.yml | 1 - 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/src/main/resources/application-staging.properties b/backend/src/main/resources/application-staging.properties index 31ce92a3c2..a5bd47acf1 100644 --- a/backend/src/main/resources/application-staging.properties +++ b/backend/src/main/resources/application-staging.properties @@ -1,10 +1,14 @@ # logging level for staging logging.level.org.springframework=debug +logging.level.ch.puzzle.okr=DEBUG +logging.level.org.springframework.security=DEBUG +logging.level.org.flywaydb.core=DEBUG + connect.src=http://localhost:8544 http://localhost:8545 -hibernate.connection.url=jdbc:postgresql://okr-dev-db:5432/okr +hibernate.connection.url=jdbc:postgresql://localhost:5432/okr hibernate.connection.username=user hibernate.connection.password=pwd hibernate.multiTenancy=SCHEMA @@ -16,7 +20,7 @@ okr.datasource.driver-class-name=org.postgresql.Driver okr.user.champion.usernames=peggimann # pitc -okr.tenants.pitc.datasource.url=jdbc:postgresql://okr-dev-db:5432/okr +okr.tenants.pitc.datasource.url=jdbc:postgresql://localhost:5432/okr okr.tenants.pitc.datasource.username=user okr.tenants.pitc.datasource.password=pwd okr.tenants.pitc.datasource.schema=okr_pitc @@ -27,7 +31,7 @@ okr.tenants.pitc.security.oauth2.frontend.client-id=pitc_okr_staging # acme -okr.tenants.acme.datasource.url=jdbc:postgresql://okr-dev-db:5432/okr +okr.tenants.acme.datasource.url=jdbc:postgresql://localhost:5432/okr okr.tenants.acme.datasource.username=user okr.tenants.acme.datasource.password=pwd okr.tenants.acme.datasource.schema=okr_acme diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index f51e73d08f..acd4766275 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -10,7 +10,6 @@ services: restart: always environment: SPRING_PROFILES_ACTIVE: staging - LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: debug SPRING_FLYWAY_LOCATIONS: classpath:db/migration,classpath:db/data-migration,classpath:db/callback volumes: - ../../../okr/backend/target:/app-root/backend From 8198471ec7dedded283c529096080004b384e241 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 15 Oct 2024 12:10:39 +0200 Subject: [PATCH 05/30] try to fix autorestart of spring --- backend/src/main/resources/application-staging.properties | 2 +- docker/dev-with-prod/docker-compose.yml | 3 +++ docker/local-prod/local-prod.Dockerfile | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/main/resources/application-staging.properties b/backend/src/main/resources/application-staging.properties index a5bd47acf1..2ec747b677 100644 --- a/backend/src/main/resources/application-staging.properties +++ b/backend/src/main/resources/application-staging.properties @@ -2,7 +2,7 @@ logging.level.org.springframework=debug logging.level.ch.puzzle.okr=DEBUG logging.level.org.springframework.security=DEBUG -logging.level.org.flywaydb.core=DEBUG +#logging.level.org.flywaydb.core=DEBUG connect.src=http://localhost:8544 http://localhost:8545 diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index acd4766275..cda96d4597 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -18,6 +18,7 @@ services: maven: tty: true container_name: maven + restart: on-failure image: maven:3.9.9-amazoncorretto-21 command: sh -c "mvn -B clean package -P build-for-docker,debug && mvn fizzed-watcher:run" working_dir: /app-root/ @@ -29,6 +30,8 @@ services: container_name: angular image: node:20 tty: true + restart: on-failure volumes: - ../../../okr:/opt command: [ "/bin/bash", "-c", "cd /opt/frontend && mkdir -p dist && npm ci && npm run watch:prod" ] + diff --git a/docker/local-prod/local-prod.Dockerfile b/docker/local-prod/local-prod.Dockerfile index c224fc3c76..bdd0ad9619 100644 --- a/docker/local-prod/local-prod.Dockerfile +++ b/docker/local-prod/local-prod.Dockerfile @@ -9,4 +9,4 @@ USER 1001 WORKDIR app-root/backend -ENTRYPOINT ["/bin/sh", "-c", "export BACKEND_VERSION=$(find . -type f -name 'backend-*.jar' -print -quit | sed -n 's/.*backend-\\(.*\\)\\.jar/\\1/p'); while true; do java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005 -jar backend-${BACKEND_VERSION}.jar & pid=$!; inotifywait -e modify backend-${BACKEND_VERSION}.jar; kill -9 $pid; echo 'JAR updated. Restarting...'; done"] \ No newline at end of file +ENTRYPOINT ["/bin/sh", "-c", "pwd; export BACKEND_VERSION=$(find . -type f -name 'backend-*.jar' -print -quit | sed -n 's/.*backend-\\(.*\\)\\.jar/\\1/p'); while true; do [ -f \"backend-${BACKEND_VERSION}.jar\" ] && (java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005 -jar backend-${BACKEND_VERSION}.jar & pid=$!; inotifywait -e modify backend-${BACKEND_VERSION}.jar; kill -9 $pid; echo 'JAR updated. Restarting...') || echo \"File backend-${BACKEND_VERSION}.jar does not exist.\"; done"] \ No newline at end of file From e9340ede494d57fcff629ce0803452f5371ff9c8 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 15 Oct 2024 13:54:49 +0200 Subject: [PATCH 06/30] complete auto restart of container --- docker/dev-with-prod/docker-compose.yml | 20 ++++++++++++++++++-- docker/local-prod/local-prod.Dockerfile | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index cda96d4597..aa47eee5d7 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -14,16 +14,32 @@ services: volumes: - ../../../okr/backend/target:/app-root/backend network_mode: "host" + depends_on: + maven-init: + condition: service_completed_successfully maven: tty: true container_name: maven restart: on-failure image: maven:3.9.9-amazoncorretto-21 - command: sh -c "mvn -B clean package -P build-for-docker,debug && mvn fizzed-watcher:run" + command: mvn fizzed-watcher:run working_dir: /app-root/ volumes: - - ../../../okr:/app-root/ + - ../../../okr:/app-root + - ~/.m2/repository:/root/.m2/repository + depends_on: + maven-init: + condition: service_completed_successfully + + maven-init: + tty: true + container_name: maven-init + image: maven:3.9.9-amazoncorretto-21 + command: mvn -B clean package -P build-for-docker,debug + working_dir: /app-root/ + volumes: + - ../../../okr:/app-root - ~/.m2/repository:/root/.m2/repository angular: diff --git a/docker/local-prod/local-prod.Dockerfile b/docker/local-prod/local-prod.Dockerfile index bdd0ad9619..e69622add5 100644 --- a/docker/local-prod/local-prod.Dockerfile +++ b/docker/local-prod/local-prod.Dockerfile @@ -9,4 +9,4 @@ USER 1001 WORKDIR app-root/backend -ENTRYPOINT ["/bin/sh", "-c", "pwd; export BACKEND_VERSION=$(find . -type f -name 'backend-*.jar' -print -quit | sed -n 's/.*backend-\\(.*\\)\\.jar/\\1/p'); while true; do [ -f \"backend-${BACKEND_VERSION}.jar\" ] && (java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005 -jar backend-${BACKEND_VERSION}.jar & pid=$!; inotifywait -e modify backend-${BACKEND_VERSION}.jar; kill -9 $pid; echo 'JAR updated. Restarting...') || echo \"File backend-${BACKEND_VERSION}.jar does not exist.\"; done"] \ No newline at end of file +ENTRYPOINT ["/bin/sh", "-c", "export BACKEND_VERSION=$(find . -type f -name 'backend-*.jar' -print -quit | sed -n 's/.*backend-\\(.*\\)\\.jar/\\1/p'); while true; do java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005 -jar backend-${BACKEND_VERSION}.jar & pid=$!; inotifywait -e modify backend-${BACKEND_VERSION}.jar; exit 1; done"] \ No newline at end of file From 2a5a31b51381714d2926b3f30fe7f04fc6ffcdc0 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Thu, 17 Oct 2024 08:28:59 +0200 Subject: [PATCH 07/30] rename intelij config and change log level of spring to debug in staging config --- .run/OkrApplication-local-prod-debug.run.xml | 17 ----------------- .../resources/application-staging.properties | 1 - docker/dev-with-prod/docker-compose.yml | 2 ++ 3 files changed, 2 insertions(+), 18 deletions(-) delete mode 100755 .run/OkrApplication-local-prod-debug.run.xml diff --git a/.run/OkrApplication-local-prod-debug.run.xml b/.run/OkrApplication-local-prod-debug.run.xml deleted file mode 100755 index 15ae63f4ef..0000000000 --- a/.run/OkrApplication-local-prod-debug.run.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/backend/src/main/resources/application-staging.properties b/backend/src/main/resources/application-staging.properties index 2ec747b677..2b162fbe66 100644 --- a/backend/src/main/resources/application-staging.properties +++ b/backend/src/main/resources/application-staging.properties @@ -1,7 +1,6 @@ # logging level for staging logging.level.org.springframework=debug logging.level.ch.puzzle.okr=DEBUG -logging.level.org.springframework.security=DEBUG #logging.level.org.flywaydb.core=DEBUG diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index aa47eee5d7..92ff3ff548 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -10,6 +10,8 @@ services: restart: always environment: SPRING_PROFILES_ACTIVE: staging + LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: DEBUG + LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_SECURITY: DEBUG SPRING_FLYWAY_LOCATIONS: classpath:db/migration,classpath:db/data-migration,classpath:db/callback volumes: - ../../../okr/backend/target:/app-root/backend From 319aa9ef497b9e7039a811ebd3548ce263c68437 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Fri, 18 Oct 2024 11:15:01 +0200 Subject: [PATCH 08/30] rename folder --- docker/dev-with-prod/docker-compose.yml | 5 +---- docker/local-prod/local-prod.Dockerfile | 12 ------------ 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 docker/local-prod/local-prod.Dockerfile diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index 92ff3ff548..2800e8111d 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -9,10 +9,7 @@ services: dockerfile: local-prod.Dockerfile restart: always environment: - SPRING_PROFILES_ACTIVE: staging - LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: DEBUG - LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_SECURITY: DEBUG - SPRING_FLYWAY_LOCATIONS: classpath:db/migration,classpath:db/data-migration,classpath:db/callback + SPRING_PROFILES_ACTIVE: dev volumes: - ../../../okr/backend/target:/app-root/backend network_mode: "host" diff --git a/docker/local-prod/local-prod.Dockerfile b/docker/local-prod/local-prod.Dockerfile deleted file mode 100644 index e69622add5..0000000000 --- a/docker/local-prod/local-prod.Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM alpine:3.20 - -USER root - -RUN apk update && apk add --upgrade curl && apk --no-cache add openjdk17 inotify-tools - -RUN adduser --home /app-root --uid 1001 --disabled-password okr -USER 1001 - -WORKDIR app-root/backend - -ENTRYPOINT ["/bin/sh", "-c", "export BACKEND_VERSION=$(find . -type f -name 'backend-*.jar' -print -quit | sed -n 's/.*backend-\\(.*\\)\\.jar/\\1/p'); while true; do java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005 -jar backend-${BACKEND_VERSION}.jar & pid=$!; inotifywait -e modify backend-${BACKEND_VERSION}.jar; exit 1; done"] \ No newline at end of file From 8c6aa04173075932e23db3761610a3f32febcf5b Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Fri, 18 Oct 2024 12:02:52 +0200 Subject: [PATCH 09/30] update docker compose file --- docker/dev-with-prod/docker-compose.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index 2800e8111d..f45ffd52f8 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -40,6 +40,9 @@ services: volumes: - ../../../okr:/app-root - ~/.m2/repository:/root/.m2/repository + depends_on: + angular: + condition: service_healthy angular: container_name: angular @@ -48,5 +51,11 @@ services: restart: on-failure volumes: - ../../../okr:/opt - command: [ "/bin/bash", "-c", "cd /opt/frontend && mkdir -p dist && npm ci && npm run watch:prod" ] + command: [ "/bin/bash", "-c", "cd /opt/frontend && rm -rf dist && mkdir -p dist && npm ci && npm run watch:prod" ] + healthcheck: + test: bash -c "[ -f /opt/frontend/dist/frontend/index.html ]" + interval: 10s + retries: 999 + start_period: 30s + timeout: 10s From dcff7653e8559678427c21d513e9273d32392eea Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Fri, 18 Oct 2024 12:25:52 +0200 Subject: [PATCH 10/30] use external profile to disable formatter --- docker/dev-with-prod/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index f45ffd52f8..44713cc772 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -35,7 +35,7 @@ services: tty: true container_name: maven-init image: maven:3.9.9-amazoncorretto-21 - command: mvn -B clean package -P build-for-docker,debug + command: mvn -B clean package -P build-for-docker,debug,no-formatter working_dir: /app-root/ volumes: - ../../../okr:/app-root From 7f637c30038581eb61d2b7169ad626e0658fa1bf Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Fri, 18 Oct 2024 12:28:39 +0200 Subject: [PATCH 11/30] clean up --- docker/dev-with-prod/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index 44713cc772..f6ccdcee2c 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -51,7 +51,7 @@ services: restart: on-failure volumes: - ../../../okr:/opt - command: [ "/bin/bash", "-c", "cd /opt/frontend && rm -rf dist && mkdir -p dist && npm ci && npm run watch:prod" ] + command: [ "/bin/bash", "-c", "cd /opt/frontend && rm -rf dist && npm ci && npm run watch:prod" ] healthcheck: test: bash -c "[ -f /opt/frontend/dist/frontend/index.html ]" interval: 10s From 858f3ed828754becf4155ca4733076700b9a0e46 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 15 Oct 2024 10:57:07 +0200 Subject: [PATCH 12/30] add jar debug dev tools only on profile --- backend/pom.xml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/backend/pom.xml b/backend/pom.xml index c08dbe7279..a5db67d5da 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -83,15 +83,6 @@ 3.26.3 test - - org.springframework.boot - spring-boot-devtools - - - org.springframework - springloaded - 1.2.8.RELEASE - @@ -320,5 +311,19 @@ + + debug + + + org.springframework.boot + spring-boot-devtools + + + org.springframework + springloaded + 1.2.8.RELEASE + + + From 90b8e4351c46fd4d8ed87c7a8d840006d0878946 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Tue, 15 Oct 2024 16:28:58 +0200 Subject: [PATCH 13/30] add api endpoint --- .../java/ch/puzzle/okr/controller/QuarterController.java | 8 ++++++++ frontend/src/app/services/quarter.service.ts | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java b/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java index eb92ef5841..34def04c1e 100644 --- a/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java +++ b/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java @@ -33,4 +33,12 @@ public QuarterController(QuarterBusinessService quarterBusinessService) { public ResponseEntity> getCurrentQuarters() { return ResponseEntity.status(HttpStatus.OK).body(this.quarterBusinessService.getQuarters()); } + + @Operation(summary = "Get quarters", description = "Get a List of quarters depending on current date") + @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Returned a List of quarters", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = TeamDto.class)) }) }) + @GetMapping("/current") + public ResponseEntity getCurrentQuarter() { + return ResponseEntity.status(HttpStatus.OK).body(this.quarterBusinessService.getCurrentQuarter()); + } } diff --git a/frontend/src/app/services/quarter.service.ts b/frontend/src/app/services/quarter.service.ts index 3991320476..875f0046a4 100644 --- a/frontend/src/app/services/quarter.service.ts +++ b/frontend/src/app/services/quarter.service.ts @@ -12,4 +12,8 @@ export class QuarterService { getAllQuarters(): Observable { return this.http.get('/api/v1/quarters'); } + + getCurrentQuarter(): Observable { + return this.http.get('/api/v1/quarters/current'); + } } From d42387e346b037f5d61a7122fc454d15bf3c0694 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Thu, 17 Oct 2024 09:33:51 +0200 Subject: [PATCH 14/30] use api to get current quarter --- .../quarter-filter.component.ts | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/components/quarter-filter/quarter-filter.component.ts b/frontend/src/app/components/quarter-filter/quarter-filter.component.ts index 1374bbc135..d2f90cf37a 100644 --- a/frontend/src/app/components/quarter-filter/quarter-filter.component.ts +++ b/frontend/src/app/components/quarter-filter/quarter-filter.component.ts @@ -25,22 +25,24 @@ export class QuarterFilterComponent implements OnInit { ngOnInit() { this.quarterService.getAllQuarters().subscribe((quarters) => { - this.quarters.next(quarters); - const quarterQuery = this.route.snapshot.queryParams['quarter']; - const quarterId: number = getValueFromQuery(quarterQuery)[0]; - if (quarters.map((quarter) => quarter.id).includes(quarterId)) { - this.quarterId = quarterId; - this.changeDisplayedQuarter(); - } else { - this.quarterId = quarters[2].id; - if (quarterQuery !== undefined) { + this.quarterService.getCurrentQuarter().subscribe((currentQuarter) => { + this.quarters.next(quarters); + const quarterQuery = this.route.snapshot.queryParams['quarter']; + const quarterId: number = getValueFromQuery(quarterQuery)[0]; + if (quarters.map((quarter) => quarter.id).includes(quarterId)) { + this.quarterId = quarterId; this.changeDisplayedQuarter(); } else { - this.refreshDataService.quarterFilterReady.next(); + this.quarterId = currentQuarter.id; + if (quarterQuery !== undefined) { + this.changeDisplayedQuarter(); + } else { + this.refreshDataService.quarterFilterReady.next(); + } } - } - const quarterLabel = quarters.find((e) => e.id == this.quarterId)?.label || ''; - this.quarterLabel$.next(quarterLabel); + const quarterLabel = quarters.find((e) => e.id == this.quarterId)?.label || ''; + this.quarterLabel$.next(quarterLabel); + }); }); } From d876f80a4882d48242bd21bb2d6d112cb6c1375c Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Thu, 17 Oct 2024 11:20:15 +0200 Subject: [PATCH 15/30] convert quarter to a class instead of an interface --- .../quarter-filter.component.html | 6 +-- .../quarter-filter.component.spec.ts | 6 +-- .../quarter-filter.component.ts | 42 +++++++++---------- frontend/src/app/services/quarter.service.ts | 10 ++++- frontend/src/app/shared/common.ts | 5 +-- .../objective-form.component.html | 2 +- .../objective-form.component.spec.ts | 15 +++---- .../objective-form.component.ts | 5 +-- .../src/app/shared/types/model/Quarter.ts | 28 ++++++++++--- 9 files changed, 66 insertions(+), 53 deletions(-) diff --git a/frontend/src/app/components/quarter-filter/quarter-filter.component.html b/frontend/src/app/components/quarter-filter/quarter-filter.component.html index 14f0ca9cd9..6d5fd442b8 100644 --- a/frontend/src/app/components/quarter-filter/quarter-filter.component.html +++ b/frontend/src/app/components/quarter-filter/quarter-filter.component.html @@ -1,12 +1,12 @@ - - {{ getQuarterLabel(quarter, i) }} + + {{ quarter.fullLabel() }} diff --git a/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts b/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts index e2507f74ce..bedb9bc652 100644 --- a/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts +++ b/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts @@ -72,7 +72,7 @@ describe('QuarterFilterComponent', () => { expect(quarterSelect).toBeTruthy(); component.ngOnInit(); fixture.detectChanges(); - expect(component.quarterId).toBe(quarters[2].id); + expect(component.currentQuarterId).toBe(quarters[2].id); expect(await quarterSelect.getValueText()).toBe(quarters[2].label + ' Aktuell'); expect(component.changeDisplayedQuarter).toHaveBeenCalledTimes(0); }); @@ -89,7 +89,7 @@ describe('QuarterFilterComponent', () => { component.ngOnInit(); fixture.detectChanges(); - expect(component.quarterId).toBe(quarters[3].id); + expect(component.currentQuarterId).toBe(quarters[3].id); expect(await quarterSelect.getValueText()).toBe(quarters[3].label); expect(component.changeDisplayedQuarter).toHaveBeenCalledTimes(1); }); @@ -106,7 +106,7 @@ describe('QuarterFilterComponent', () => { routerHarness.detectChanges(); component.ngOnInit(); fixture.detectChanges(); - expect(component.quarterId).toBe(quarters[2].id); + expect(component.currentQuarterId).toBe(quarters[2].id); expect(await quarterSelect.getValueText()).toBe(quarters[2].label + ' Aktuell'); expect(component.changeDisplayedQuarter).toHaveBeenCalledTimes(1); expect(router.url).toBe('/?quarter=' + quarters[2].id); diff --git a/frontend/src/app/components/quarter-filter/quarter-filter.component.ts b/frontend/src/app/components/quarter-filter/quarter-filter.component.ts index d2f90cf37a..1d62ac710a 100644 --- a/frontend/src/app/components/quarter-filter/quarter-filter.component.ts +++ b/frontend/src/app/components/quarter-filter/quarter-filter.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component, EventEmitter, OnInit, Output } from '@angular/core'; import { QuarterService } from '../../services/quarter.service'; import { Quarter } from '../../shared/types/model/Quarter'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, forkJoin } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; -import { getQuarterLabel, getValueFromQuery } from '../../shared/common'; import { RefreshDataService } from '../../services/refresh-data.service'; +import { getValueFromQuery } from '../../shared/common'; @Component({ selector: 'app-quarter-filter', @@ -14,7 +14,7 @@ import { RefreshDataService } from '../../services/refresh-data.service'; export class QuarterFilterComponent implements OnInit { quarters: BehaviorSubject = new BehaviorSubject([]); @Output() quarterLabel$ = new EventEmitter(); - quarterId: number = -1; + currentQuarterId: number = -1; constructor( private quarterService: QuarterService, @@ -24,30 +24,30 @@ export class QuarterFilterComponent implements OnInit { ) {} ngOnInit() { - this.quarterService.getAllQuarters().subscribe((quarters) => { - this.quarterService.getCurrentQuarter().subscribe((currentQuarter) => { - this.quarters.next(quarters); - const quarterQuery = this.route.snapshot.queryParams['quarter']; - const quarterId: number = getValueFromQuery(quarterQuery)[0]; - if (quarters.map((quarter) => quarter.id).includes(quarterId)) { - this.quarterId = quarterId; + const allQuarters$ = this.quarterService.getAllQuarters(); + const currentQuarter$ = this.quarterService.getCurrentQuarter(); + forkJoin([allQuarters$, currentQuarter$]).subscribe(([quarters, currentQuarter]) => { + this.quarters.next(quarters); + const quarterQuery = this.route.snapshot.queryParams['quarter']; + const quarterId: number = getValueFromQuery(quarterQuery)[0]; + if (quarters.map((quarter) => quarter.id).includes(quarterId)) { + this.currentQuarterId = quarterId; + this.changeDisplayedQuarter(); + } else { + this.currentQuarterId = currentQuarter.id; + if (quarterQuery !== undefined) { this.changeDisplayedQuarter(); } else { - this.quarterId = currentQuarter.id; - if (quarterQuery !== undefined) { - this.changeDisplayedQuarter(); - } else { - this.refreshDataService.quarterFilterReady.next(); - } + this.refreshDataService.quarterFilterReady.next(); } - const quarterLabel = quarters.find((e) => e.id == this.quarterId)?.label || ''; - this.quarterLabel$.next(quarterLabel); - }); + } + const quarterLabel = quarters.find((e) => e.id == this.currentQuarterId)?.label || ''; + this.quarterLabel$.next(quarterLabel); }); } changeDisplayedQuarter() { - const id = this.quarterId; + const id = this.currentQuarterId; const quarterLabel = this.quarters.getValue().find((e) => e.id == id)?.label || ''; this.quarterLabel$.next(quarterLabel); @@ -55,6 +55,4 @@ export class QuarterFilterComponent implements OnInit { .navigate([], { queryParams: { quarter: id } }) .then(() => this.refreshDataService.quarterFilterReady.next()); } - - protected readonly getQuarterLabel = getQuarterLabel; } diff --git a/frontend/src/app/services/quarter.service.ts b/frontend/src/app/services/quarter.service.ts index 875f0046a4..a6d4501ff8 100644 --- a/frontend/src/app/services/quarter.service.ts +++ b/frontend/src/app/services/quarter.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Quarter } from '../shared/types/model/Quarter'; -import { Observable } from 'rxjs'; +import { map, Observable } from 'rxjs'; @Injectable({ providedIn: 'root', @@ -10,7 +10,13 @@ export class QuarterService { constructor(private http: HttpClient) {} getAllQuarters(): Observable { - return this.http.get('/api/v1/quarters'); + return this.http + .get('/api/v1/quarters') + .pipe( + map((quarters) => + quarters.map((quarter) => new Quarter(quarter.id, quarter.label, quarter.startDate, quarter.endDate)), + ), + ); } getCurrentQuarter(): Observable { diff --git a/frontend/src/app/shared/common.ts b/frontend/src/app/shared/common.ts index ec0d3de2b3..bf9a3569e7 100644 --- a/frontend/src/app/shared/common.ts +++ b/frontend/src/app/shared/common.ts @@ -1,5 +1,6 @@ import { FormGroup } from '@angular/forms'; import { KeyResultMetricMin } from './types/model/KeyResultMetricMin'; +import { Quarter } from './types/model/Quarter'; export function getNumberOrNull(str: string | null | undefined): number | null { if (str === null || str === undefined || str.toString().trim() === '') { @@ -87,10 +88,6 @@ export function formInputCheck(form: FormGroup, propertyName: string) { } } -export function getQuarterLabel(quarter: any, index: number): string { - return index == 2 ? quarter.label + ' Aktuell' : quarter.label; -} - export function isMobileDevice() { return window.navigator.userAgent.toLowerCase().includes('mobile'); } diff --git a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html index 28e461b3bf..742809d8f3 100644 --- a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html +++ b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html @@ -60,7 +60,7 @@ > diff --git a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts index 47ca3ba9ff..3fff171da0 100644 --- a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts +++ b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts @@ -38,9 +38,9 @@ let objectiveService = { const quarterService = { getAllQuarters(): Observable { return of([ - { id: 1, startDate: quarter.startDate, endDate: quarter.endDate, label: quarter.label }, - { id: 2, startDate: quarter.startDate, endDate: quarter.endDate, label: quarter.label }, - { id: 999, startDate: null, endDate: null, label: 'Backlog' }, + new Quarter(1, quarter.label, quarter.startDate, quarter.endDate), + new Quarter(2, quarter.label, quarter.startDate, quarter.endDate), + new Quarter(999, 'Backlog', null, null), ]); }, }; @@ -292,12 +292,8 @@ describe('ObjectiveDialogComponent', () => { }); it('should return if option is allowed for quarter select', async () => { - let quarter: Quarter = { - id: 999, - label: 'Backlog', - startDate: null, - endDate: null, - }; + let quarter: Quarter = new Quarter(1, 'Backlog', null, null); + let data = { action: 'duplicate', objective: { @@ -310,7 +306,6 @@ describe('ObjectiveDialogComponent', () => { expect(component.allowedOption(quarter)).toBeTruthy(); - quarter.label = 'Backlog'; expect(component.allowedOption(quarter)).toBeTruthy(); data.action = 'releaseBacklog'; fixture.detectChanges(); diff --git a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts index 33fbb38769..6eaf056385 100644 --- a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts +++ b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts @@ -10,7 +10,8 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { State } from '../../types/enums/State'; import { ObjectiveMin } from '../../types/model/ObjectiveMin'; import { Objective } from '../../types/model/Objective'; -import { formInputCheck, getQuarterLabel, getValueFromQuery, hasFormFieldErrors } from '../../common'; +import { ConfirmDialogComponent } from '../confirm-dialog/confirm-dialog.component'; +import { formInputCheck, getValueFromQuery, hasFormFieldErrors, isMobileDevice } from '../../common'; import { ActivatedRoute } from '@angular/router'; import { GJ_REGEX_PATTERN } from '../../constantLibary'; import { TranslateService } from '@ngx-translate/core'; @@ -215,6 +216,4 @@ export class ObjectiveFormComponent implements OnInit, OnDestroy { isBacklogQuarter(label: string) { return GJ_REGEX_PATTERN.test(label); } - - protected readonly getQuarterLabel = getQuarterLabel; } diff --git a/frontend/src/app/shared/types/model/Quarter.ts b/frontend/src/app/shared/types/model/Quarter.ts index 07d312e084..cc97f785dd 100644 --- a/frontend/src/app/shared/types/model/Quarter.ts +++ b/frontend/src/app/shared/types/model/Quarter.ts @@ -1,6 +1,24 @@ -export interface Quarter { - id: number; - label: string; - startDate: Date | null; - endDate: Date | null; +export class Quarter { + constructor(id: number, label: string, startDate: Date | null, endDate: Date | null) { + this.id = id; + this.label = label; + this.startDate = startDate; + this.endDate = endDate; + } + + readonly id: number; + readonly label: string; + readonly startDate: Date | null; + readonly endDate: Date | null; + + fullLabel(): string { + return this.isCurrent() ? this.label + ' Aktuell' : this.label; + } + + private isCurrent(): boolean { + if (this.startDate === null || this.endDate === null) { + return false; + } + return this.startDate <= new Date() && this.endDate >= new Date(); + } } From cb64b0b8ec940ec2d28ab47b27207f4cc03705bf Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Thu, 17 Oct 2024 11:30:30 +0200 Subject: [PATCH 16/30] use class syntax --- frontend/src/app/shared/testData.ts | 70 +++++------------------------ 1 file changed, 11 insertions(+), 59 deletions(-) diff --git a/frontend/src/app/shared/testData.ts b/frontend/src/app/shared/testData.ts index 9a4a64044e..48ba98a0de 100644 --- a/frontend/src/app/shared/testData.ts +++ b/frontend/src/app/shared/testData.ts @@ -90,31 +90,13 @@ export const addedAction: Action = { keyResultId: 1, } as Action; -export const quarterMin: Quarter = { - id: 1, - label: 'GJ 23/24-Q1', -} as Quarter; +export const quarterMin: Quarter = new Quarter(1, 'GJ 23/24-Q1', null, null); -export const quarter1: Quarter = { - id: 1, - label: 'GJ 22/23-Q4', - startDate: new Date('2023-04-01'), - endDate: new Date('2023-07-30'), -} as Quarter; +export const quarter1: Quarter = new Quarter(1, 'GJ 22/23-Q4', new Date('2023-04-01'), new Date('2023-07-30')); -export const quarter2: Quarter = { - id: 2, - label: 'GJ 22/23-Q3', - startDate: new Date('2023-01-01'), - endDate: new Date('2023-03-31'), -} as Quarter; - -export const quarterBacklog: Quarter = { - id: 999, - label: 'GJ 23/24-Q1', - startDate: null, - endDate: null, -} as Quarter; +export const quarter2: Quarter = new Quarter(2, 'GJ 22/23-Q3', new Date('2023-01-01'), new Date('2023-03-31')); + +export const quarterBacklog: Quarter = new Quarter(999, 'GJ 23/24-Q1', null, null); export const quarterList: Quarter[] = [quarter1, quarter2, quarterBacklog]; @@ -308,12 +290,7 @@ export const overViewEntityResponse2: any = { export const overviews: OverviewEntity[] = [overViewEntityResponse1, overViewEntityResponse2]; -export const quarter: Quarter = { - id: 1, - label: '23.02.2025', - endDate: new Date(), - startDate: new Date(), -}; +export const quarter: Quarter = new Quarter(1, '23.02.2025', new Date(), new Date()); export const keyResultObjective: KeyResultObjective = { id: 1, @@ -435,12 +412,7 @@ export const keyResult: KeyResultOrdinal = { id: 301, version: 1, state: State.DRAFT, - quarter: { - id: 1, - label: 'GJ 23/24-Q1', - startDate: new Date(), - endDate: new Date(), - } as Quarter, + quarter: new Quarter(1, 'GJ 23/24-Q1', new Date(), new Date()), writeable: true, } as KeyResultObjective, lastCheckIn: { @@ -474,12 +446,7 @@ export const keyResultOrdinal: KeyResultOrdinal = { id: 301, version: 1, state: State.DRAFT, - quarter: { - id: 1, - label: 'GJ 23/24-Q1', - startDate: new Date(), - endDate: new Date(), - } as Quarter, + quarter: new Quarter(1, 'GJ 23/24-Q1', new Date(), new Date()), writeable: true, } as KeyResultObjective, lastCheckIn: { @@ -513,12 +480,7 @@ export const keyResultWriteableFalse: KeyResultOrdinal = { id: 301, version: 1, state: State.DRAFT, - quarter: { - id: 1, - label: 'GJ 23/24-Q1', - startDate: new Date(), - endDate: new Date(), - } as Quarter, + quarter: new Quarter(1, 'GJ 23/24-Q1', new Date(), new Date()), writeable: false, } as KeyResultObjective, lastCheckIn: { @@ -552,12 +514,7 @@ export const keyResultMetric: KeyResultMetric = { id: 302, version: 1, state: State.DRAFT, - quarter: { - id: 1, - label: 'GJ 23/24-Q1', - startDate: new Date(), - endDate: new Date(), - } as Quarter, + quarter: new Quarter(1, 'GJ 23/24-Q1', new Date(), new Date()), writeable: true, } as KeyResultObjective, lastCheckIn: { @@ -590,12 +547,7 @@ export const keyResultActions: KeyResultMetric = { objective: { id: 302, state: State.DRAFT, - quarter: { - id: 1, - label: 'GJ 23/24-Q1', - startDate: new Date(), - endDate: new Date(), - } as Quarter, + quarter: new Quarter(1, 'GJ 23/24-Q1', new Date(), new Date()), writeable: true, } as KeyResultObjective, lastCheckIn: { From 1ec4c6e75808a34e983b0b0884f7dd64712c06cc Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Thu, 17 Oct 2024 11:37:03 +0200 Subject: [PATCH 17/30] update specs to use the class syntax for quarters --- .../keyresult-dialog/keyresult-dialog.component.spec.ts | 3 ++- .../quarter-filter/quarter-filter.component.spec.ts | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/components/keyresult-dialog/keyresult-dialog.component.spec.ts b/frontend/src/app/components/keyresult-dialog/keyresult-dialog.component.spec.ts index 17d9f4d017..bf74d658ce 100644 --- a/frontend/src/app/components/keyresult-dialog/keyresult-dialog.component.spec.ts +++ b/frontend/src/app/components/keyresult-dialog/keyresult-dialog.component.spec.ts @@ -24,6 +24,7 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { UserService } from '../../services/user.service'; import { KeyResultFormComponent } from '../key-result-form/key-result-form.component'; +import { Quarter } from '../../shared/types/model/Quarter'; describe('KeyresultDialogComponent', () => { let component: KeyresultDialogComponent; @@ -55,7 +56,7 @@ describe('KeyresultDialogComponent', () => { let keyResultObjective: KeyResultObjective = { id: 2, state: State.ONGOING, - quarter: { id: 1, label: 'GJ 22/23-Q2', endDate: new Date(), startDate: new Date() }, + quarter: new Quarter(1, 'GJ 22/23-Q2', new Date(), new Date()), }; let fullKeyResultMetric = { diff --git a/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts b/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts index bedb9bc652..b81c7056e9 100644 --- a/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts +++ b/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts @@ -21,10 +21,10 @@ const overviewService = { }; const quarters = [ - { id: 999, label: 'Backlog', startDate: null, endDate: null }, - { ...quarter, id: 2 }, - { ...quarter, id: 5 }, - { ...quarter, id: 7 }, + new Quarter(999, 'Backlog', null, null), + new Quarter(2, '23.02.2025', new Date(), new Date()), + new Quarter(5, '23.02.2025', new Date(), new Date()), + new Quarter(7, '23.02.2025', new Date(), new Date()), ]; const quarterService = { From eba27cfc628f687f4aac234e2de9e7c21d1e758f Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Thu, 17 Oct 2024 11:53:20 +0200 Subject: [PATCH 18/30] mock new class function in test --- .../key-result-form/key-result-form.component.spec.ts | 3 ++- .../quarter-filter/quarter-filter.component.spec.ts | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts b/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts index 724f517822..d0d2479b8a 100644 --- a/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts +++ b/frontend/src/app/components/key-result-form/key-result-form.component.spec.ts @@ -27,6 +27,7 @@ import { KeyResultMetric } from '../../shared/types/model/KeyResultMetric'; import { KeyResultOrdinal } from '../../shared/types/model/KeyResultOrdinal'; import { TranslateTestingModule } from 'ngx-translate-testing'; import * as de from '../../../assets/i18n/de.json'; +import { Quarter } from '../../shared/types/model/Quarter'; describe('KeyResultFormComponent', () => { let component: KeyResultFormComponent; @@ -70,7 +71,7 @@ describe('KeyResultFormComponent', () => { const keyResultObjective: KeyResultObjective = { id: 2, state: State.ONGOING, - quarter: { id: 1, label: 'GJ 22/23-Q2', endDate: new Date(), startDate: new Date() }, + quarter: new Quarter(1, 'GJ 22/23-Q2', new Date(), new Date()), }; const keyResultFormGroup = new FormGroup({ diff --git a/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts b/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts index b81c7056e9..acec7442dc 100644 --- a/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts +++ b/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts @@ -2,7 +2,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { QuarterFilterComponent } from './quarter-filter.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { OverviewService } from '../../services/overview.service'; -import { quarter } from '../../shared/testData'; import { Observable, of } from 'rxjs'; import { Quarter } from '../../shared/types/model/Quarter'; import { QuarterService } from '../../services/quarter.service'; @@ -31,6 +30,9 @@ const quarterService = { getAllQuarters(): Observable { return of(quarters); }, + getCurrentQuarter(): Observable { + return of(quarters[2]); + }, }; describe('QuarterFilterComponent', () => { @@ -68,6 +70,7 @@ describe('QuarterFilterComponent', () => { it('should set correct default quarter if no route param is defined', async () => { jest.spyOn(component, 'changeDisplayedQuarter'); + jest.spyOn(quarters[2] as any, 'isCurrent').mockReturnValue(true); const quarterSelect = await loader.getHarness(MatSelectHarness); expect(quarterSelect).toBeTruthy(); component.ngOnInit(); From 9419584f71a21c33255d28a21288bf5f3cf5c523 Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Thu, 17 Oct 2024 13:35:52 +0200 Subject: [PATCH 19/30] add integratin test for current quarter endpoint --- .../ch/puzzle/okr/controller/QuarterControllerIT.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java b/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java index cfd844139f..e844454b5b 100644 --- a/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java +++ b/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.BDDMockito; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -67,4 +68,11 @@ void shouldGetAllTeamsIfNoTeamsExists() throws Exception { mvc.perform(get("/api/v1/quarters").contentType(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()).andExpect(jsonPath("$", Matchers.hasSize(0))); } -} \ No newline at end of file + + @Test + void shouldCallCurrentQuarterAfterRequest() throws Exception { + mvc.perform(get("/api/v1/quarters/current").contentType(MediaType.APPLICATION_JSON)); + + BDDMockito.verify(quarterBusinessService, Mockito.times(1)).getCurrentQuarter(); + } +} From 1bc70e9fda7a2d367a06edcba3ea9b035793e46f Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Mon, 21 Oct 2024 09:03:39 +0200 Subject: [PATCH 20/30] fix default quarter for new objective selection --- .../quarter-filter/quarter-filter.component.spec.ts | 2 +- .../components/quarter-filter/quarter-filter.component.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts b/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts index acec7442dc..bcd2e4bdd5 100644 --- a/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts +++ b/frontend/src/app/components/quarter-filter/quarter-filter.component.spec.ts @@ -77,7 +77,7 @@ describe('QuarterFilterComponent', () => { fixture.detectChanges(); expect(component.currentQuarterId).toBe(quarters[2].id); expect(await quarterSelect.getValueText()).toBe(quarters[2].label + ' Aktuell'); - expect(component.changeDisplayedQuarter).toHaveBeenCalledTimes(0); + expect(component.changeDisplayedQuarter).toHaveBeenCalledTimes(1); }); it('should set correct value in form according to route param', async () => { diff --git a/frontend/src/app/components/quarter-filter/quarter-filter.component.ts b/frontend/src/app/components/quarter-filter/quarter-filter.component.ts index 1d62ac710a..023a242adf 100644 --- a/frontend/src/app/components/quarter-filter/quarter-filter.component.ts +++ b/frontend/src/app/components/quarter-filter/quarter-filter.component.ts @@ -35,9 +35,9 @@ export class QuarterFilterComponent implements OnInit { this.changeDisplayedQuarter(); } else { this.currentQuarterId = currentQuarter.id; - if (quarterQuery !== undefined) { - this.changeDisplayedQuarter(); - } else { + this.changeDisplayedQuarter(); + + if (quarterQuery === undefined) { this.refreshDataService.quarterFilterReady.next(); } } From f9fe3265bac03963afcfcbbb21a2b64ef89740ca Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Mon, 21 Oct 2024 09:06:53 +0200 Subject: [PATCH 21/30] use v2 for quarter url and update tests accoridingly --- .../java/ch/puzzle/okr/controller/QuarterController.java | 2 +- .../java/ch/puzzle/okr/controller/QuarterControllerIT.java | 6 +++--- frontend/src/app/services/quarter.service.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java b/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java index 34def04c1e..8f19714dfe 100644 --- a/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java +++ b/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java @@ -17,7 +17,7 @@ import java.util.List; @RestController -@RequestMapping("api/v1/quarters") +@RequestMapping("api/v2/quarters") public class QuarterController { private final QuarterBusinessService quarterBusinessService; diff --git a/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java b/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java index e844454b5b..5a582cd117 100644 --- a/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java +++ b/backend/src/test/java/ch/puzzle/okr/controller/QuarterControllerIT.java @@ -49,7 +49,7 @@ class QuarterControllerIT { void shouldGetAllQuarters() throws Exception { BDDMockito.given(quarterBusinessService.getQuarters()).willReturn(quaterList); - mvc.perform(get("/api/v1/quarters").contentType(MediaType.APPLICATION_JSON)) + mvc.perform(get("/api/v2/quarters").contentType(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()).andExpect(jsonPath("$", Matchers.hasSize(3))) .andExpect(jsonPath("$[0].id", Is.is(1))).andExpect(jsonPath("$[0].label", Is.is("GJ 22/23-Q2"))) .andExpect(jsonPath("$[0].startDate", Is.is(LocalDate.of(2022, 9, 1).toString()))) @@ -65,13 +65,13 @@ void shouldGetAllQuarters() throws Exception { void shouldGetAllTeamsIfNoTeamsExists() throws Exception { BDDMockito.given(quarterBusinessService.getQuarters()).willReturn(Collections.emptyList()); - mvc.perform(get("/api/v1/quarters").contentType(MediaType.APPLICATION_JSON)) + mvc.perform(get("/api/v2/quarters").contentType(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()).andExpect(jsonPath("$", Matchers.hasSize(0))); } @Test void shouldCallCurrentQuarterAfterRequest() throws Exception { - mvc.perform(get("/api/v1/quarters/current").contentType(MediaType.APPLICATION_JSON)); + mvc.perform(get("/api/v2/quarters/current").contentType(MediaType.APPLICATION_JSON)); BDDMockito.verify(quarterBusinessService, Mockito.times(1)).getCurrentQuarter(); } diff --git a/frontend/src/app/services/quarter.service.ts b/frontend/src/app/services/quarter.service.ts index a6d4501ff8..a811c6052e 100644 --- a/frontend/src/app/services/quarter.service.ts +++ b/frontend/src/app/services/quarter.service.ts @@ -11,7 +11,7 @@ export class QuarterService { getAllQuarters(): Observable { return this.http - .get('/api/v1/quarters') + .get('/api/v2/quarters') .pipe( map((quarters) => quarters.map((quarter) => new Quarter(quarter.id, quarter.label, quarter.startDate, quarter.endDate)), @@ -20,6 +20,6 @@ export class QuarterService { } getCurrentQuarter(): Observable { - return this.http.get('/api/v1/quarters/current'); + return this.http.get('/api/v2/quarters/current'); } } From c3d3b37286f8f0a7c7de5dfbbcfb9f71b08f1d4c Mon Sep 17 00:00:00 2001 From: Miguel Lehmann Date: Mon, 21 Oct 2024 14:49:16 +0200 Subject: [PATCH 22/30] change merhod to fakeAsync --- .../objective-form.component.spec.ts | 123 +++++++++--------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts index 3fff171da0..cb36f41a3c 100644 --- a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts +++ b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { ObjectiveFormComponent } from './objective-form.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; @@ -115,65 +115,68 @@ describe('ObjectiveDialogComponent', () => { expect(component).toBeTruthy(); }); - it.each([['DRAFT'], ['ONGOING']])('onSubmit create', async (state: string) => { - //Prepare data - let title: string = 'title'; - let description: string = 'description'; - let createKeyresults: boolean = true; - let quarter: number = 0; - let team: number = 0; - teamService.getAllTeams().subscribe((teams) => { - team = teams[0].id; - }); - quarterService.getAllQuarters().subscribe((quarters) => { - quarter = quarters[1].id; - }); - - // Get input elements and set values - const titleInput: HTMLInputElement = fixture.debugElement.query(By.css('[data-testId="title"]')).nativeElement; - titleInput.value = title; - const descriptionInput: HTMLInputElement = fixture.debugElement.query( - By.css('[data-testId="description"]'), - ).nativeElement; - descriptionInput.value = description; - const checkBox = await loader.getHarness(MatCheckboxHarness); - await checkBox.check(); - const quarterSelect: HTMLSelectElement = fixture.debugElement.query(By.css('#quarter')).nativeElement; - quarterSelect.value = quarter.toString(); - - // Trigger update of form - fixture.detectChanges(); - titleInput.dispatchEvent(new Event('input')); - descriptionInput.dispatchEvent(new Event('input')); - quarterSelect.dispatchEvent(new Event('input')); - - const rawFormValue = component.objectiveForm.getRawValue(); - expect(rawFormValue.description).toBe(description); - expect(rawFormValue.quarter).toBe(quarter); - expect(rawFormValue.team).toBe(team); - expect(rawFormValue.title).toBe(title); - expect(rawFormValue.createKeyResults).toBe(createKeyresults); - - objectiveService.createObjective.mockReturnValue(of({ ...objective, state: state })); - component.onSubmit(state); - - expect(dialogMock.close).toHaveBeenCalledWith({ - addKeyResult: createKeyresults, - delete: false, - objective: { - description: description, - id: 5, - version: 1, - quarterId: 2, - quarterLabel: 'GJ 22/23-Q2', - state: State[state as keyof typeof State], - teamId: 2, - title: title, - writeable: true, - }, - teamId: 1, - }); - }); + it.each([['DRAFT'], ['ONGOING']])( + 'onSubmit create', + fakeAsync((state: string) => { + //Prepare data + let title: string = 'title'; + let description: string = 'description'; + let createKeyresults: boolean = true; + let quarter: number = 0; + let team: number = 0; + teamService.getAllTeams().subscribe((teams) => { + team = teams[0].id; + }); + quarterService.getAllQuarters().subscribe((quarters) => { + quarter = quarters[1].id; + }); + + // Get input elements and set values + const titleInput: HTMLInputElement = fixture.debugElement.query(By.css('[data-testId="title"]')).nativeElement; + titleInput.value = title; + const descriptionInput: HTMLInputElement = fixture.debugElement.query( + By.css('[data-testId="description"]'), + ).nativeElement; + descriptionInput.value = description; + loader.getHarness(MatCheckboxHarness).then((checkBox) => checkBox.check()); + tick(200); + const quarterSelect: HTMLSelectElement = fixture.debugElement.query(By.css('#quarter')).nativeElement; + quarterSelect.value = quarter.toString(); + + // Trigger update of form + fixture.detectChanges(); + titleInput.dispatchEvent(new Event('input')); + descriptionInput.dispatchEvent(new Event('input')); + quarterSelect.dispatchEvent(new Event('input')); + + const rawFormValue = component.objectiveForm.getRawValue(); + expect(rawFormValue.description).toBe(description); + expect(rawFormValue.quarter).toBe(quarter); + expect(rawFormValue.team).toBe(team); + expect(rawFormValue.title).toBe(title); + expect(rawFormValue.createKeyResults).toBe(createKeyresults); + + objectiveService.createObjective.mockReturnValue(of({ ...objective, state: state })); + component.onSubmit(state); + + expect(dialogMock.close).toHaveBeenCalledWith({ + addKeyResult: createKeyresults, + delete: false, + objective: { + description: description, + id: 5, + version: 1, + quarterId: 2, + quarterLabel: 'GJ 22/23-Q2', + state: State[state as keyof typeof State], + teamId: 2, + title: title, + writeable: true, + }, + teamId: 1, + }); + }), + ); it('should create objective', () => { matDataMock.objective.objectiveId = undefined; From 8ae8de2e9703af9192658eb619585798fb1e8ee7 Mon Sep 17 00:00:00 2001 From: Miguel Lehmann Date: Tue, 22 Oct 2024 12:54:03 +0200 Subject: [PATCH 23/30] try to override provider mid test --- .../objective-form.component.spec.ts | 20 +++++++++++++++++-- .../objective-form.component.ts | 9 +++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts index cb36f41a3c..4a73b6ba5b 100644 --- a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts +++ b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts @@ -35,6 +35,10 @@ let objectiveService = { deleteObjective: jest.fn(), }; +interface MatDialogDataInterface { + objective: { objectiveId: number | undefined; teamId: number | undefined }; +} + const quarterService = { getAllQuarters(): Observable { return of([ @@ -43,6 +47,9 @@ const quarterService = { new Quarter(999, 'Backlog', null, null), ]); }, + getCurrentQuarter(): Observable { + return of(new Quarter(2, quarter.label, quarter.startDate, quarter.endDate)); + }, }; const teamService = { @@ -58,7 +65,7 @@ const dialogMock = { close: jest.fn(), }; -let matDataMock: { objective: { objectiveId: number | undefined; teamId: number | undefined } } = { +let matDataMock: MatDialogDataInterface = { objective: { objectiveId: undefined, teamId: 1, @@ -118,6 +125,16 @@ describe('ObjectiveDialogComponent', () => { it.each([['DRAFT'], ['ONGOING']])( 'onSubmit create', fakeAsync((state: string) => { + matDataMock = { + objectiveId: 99, + teamId: 1, + } as any as MatDialogDataInterface; + + TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: matDataMock }); + TestBed.compileComponents(); + component = fixture.componentInstance; + fixture.detectChanges(); + //Prepare data let title: string = 'title'; let description: string = 'description'; @@ -142,7 +159,6 @@ describe('ObjectiveDialogComponent', () => { tick(200); const quarterSelect: HTMLSelectElement = fixture.debugElement.query(By.css('#quarter')).nativeElement; quarterSelect.value = quarter.toString(); - // Trigger update of form fixture.detectChanges(); titleInput.dispatchEvent(new Event('input')); diff --git a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts index 6eaf056385..9875795967 100644 --- a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts +++ b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.ts @@ -33,6 +33,7 @@ export class ObjectiveFormComponent implements OnInit, OnDestroy { createKeyResults: new FormControl(false), }); quarters$: Observable = of([]); + currentQuarter$: Observable = of(); quarters: Quarter[] = []; teams$: Observable = of([]); currentTeam: Subject = new Subject(); @@ -83,16 +84,16 @@ export class ObjectiveFormComponent implements OnInit, OnDestroy { const isCreating: boolean = !!this.data.objective.objectiveId; this.teams$ = this.teamService.getAllTeams().pipe(takeUntil(this.unsubscribe$)); this.quarters$ = this.quarterService.getAllQuarters(); + this.currentQuarter$ = this.quarterService.getCurrentQuarter(); const objective$ = isCreating ? this.objectiveService.getFullObjective(this.data.objective.objectiveId!) : of(this.getDefaultObjective()); - - forkJoin([objective$, this.quarters$]).subscribe(([objective, quarters]) => { + forkJoin([objective$, this.quarters$, this.currentQuarter$]).subscribe(([objective, quarters, currentQuarter]) => { this.quarters = quarters; const teamId = isCreating ? objective.teamId : this.data.objective.teamId; - let quarterId = getValueFromQuery(this.route.snapshot.queryParams['quarter'], quarters[1].id)[0]; + const newEditQuarter = isCreating ? currentQuarter.id : objective.quarterId; + let quarterId = getValueFromQuery(this.route.snapshot.queryParams['quarter'], newEditQuarter)[0]; - let currentQuarter: Quarter | undefined = this.quarters.find((quarter) => quarter.id == quarterId); if (currentQuarter && !this.isBacklogQuarter(currentQuarter.label) && this.data.action == 'releaseBacklog') { quarterId = quarters[1].id; } From a4f83a1e9002a1767ca984d4c1d88114683d401c Mon Sep 17 00:00:00 2001 From: Jannik Pulfer Date: Tue, 22 Oct 2024 15:14:09 +0200 Subject: [PATCH 24/30] Dispatch change instead of input event in onSubmit create test and get element by test id instead of id --- .../objective-form.component.spec.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts index 4a73b6ba5b..4e667435bc 100644 --- a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts +++ b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.spec.ts @@ -125,16 +125,6 @@ describe('ObjectiveDialogComponent', () => { it.each([['DRAFT'], ['ONGOING']])( 'onSubmit create', fakeAsync((state: string) => { - matDataMock = { - objectiveId: 99, - teamId: 1, - } as any as MatDialogDataInterface; - - TestBed.overrideProvider(MAT_DIALOG_DATA, { useValue: matDataMock }); - TestBed.compileComponents(); - component = fixture.componentInstance; - fixture.detectChanges(); - //Prepare data let title: string = 'title'; let description: string = 'description'; @@ -157,17 +147,19 @@ describe('ObjectiveDialogComponent', () => { descriptionInput.value = description; loader.getHarness(MatCheckboxHarness).then((checkBox) => checkBox.check()); tick(200); - const quarterSelect: HTMLSelectElement = fixture.debugElement.query(By.css('#quarter')).nativeElement; + const quarterSelect: HTMLSelectElement = fixture.debugElement.query( + By.css('[data-testId="quarterSelect"]'), + ).nativeElement; quarterSelect.value = quarter.toString(); // Trigger update of form fixture.detectChanges(); titleInput.dispatchEvent(new Event('input')); descriptionInput.dispatchEvent(new Event('input')); - quarterSelect.dispatchEvent(new Event('input')); + quarterSelect.dispatchEvent(new Event('change')); const rawFormValue = component.objectiveForm.getRawValue(); expect(rawFormValue.description).toBe(description); - expect(rawFormValue.quarter).toBe(quarter); + expect(rawFormValue.quarter).toBe(quarter.toString()); expect(rawFormValue.team).toBe(team); expect(rawFormValue.title).toBe(title); expect(rawFormValue.createKeyResults).toBe(createKeyresults); From 85c499e0a6ab6ef66a6279b9a9b509eaca31d9ff Mon Sep 17 00:00:00 2001 From: Jannik Pulfer Date: Tue, 22 Oct 2024 15:56:28 +0200 Subject: [PATCH 25/30] Remove duplicate profile in pom --- backend/pom.xml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/backend/pom.xml b/backend/pom.xml index a5db67d5da..76954ba974 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -311,19 +311,5 @@ - - debug - - - org.springframework.boot - spring-boot-devtools - - - org.springframework - springloaded - 1.2.8.RELEASE - - - From 7a3a4b9b3a2fb3a5f0530c22e811cd37ee41de9e Mon Sep 17 00:00:00 2001 From: Jannik Pulfer Date: Wed, 23 Oct 2024 08:03:13 +0200 Subject: [PATCH 26/30] Update swagger annotations of get current quarter and fix usage of wrong implementation class for get current quarters --- .../java/ch/puzzle/okr/controller/QuarterController.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java b/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java index 8f19714dfe..1cc1383dbc 100644 --- a/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java +++ b/backend/src/main/java/ch/puzzle/okr/controller/QuarterController.java @@ -28,15 +28,15 @@ public QuarterController(QuarterBusinessService quarterBusinessService) { @Operation(summary = "Get quarters", description = "Get a List of quarters depending on current date") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Returned a List of quarters", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = TeamDto.class)) }) }) + @Content(mediaType = "application/json", schema = @Schema(implementation = Quarter.class)) }) }) @GetMapping("") public ResponseEntity> getCurrentQuarters() { return ResponseEntity.status(HttpStatus.OK).body(this.quarterBusinessService.getQuarters()); } - @Operation(summary = "Get quarters", description = "Get a List of quarters depending on current date") - @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Returned a List of quarters", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = TeamDto.class)) }) }) + @Operation(summary = "Get current quarter", description = "Get the current quarter depending on current date") + @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Returned the current quarter", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = Quarter.class)) }) }) @GetMapping("/current") public ResponseEntity getCurrentQuarter() { return ResponseEntity.status(HttpStatus.OK).body(this.quarterBusinessService.getCurrentQuarter()); From 4c5e7a4362decfb4bac858ff5f7175e44da3736d Mon Sep 17 00:00:00 2001 From: Jannik Pulfer Date: Wed, 23 Oct 2024 16:08:12 +0200 Subject: [PATCH 27/30] Remove unused import in common.ts --- frontend/src/app/shared/common.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/app/shared/common.ts b/frontend/src/app/shared/common.ts index bf9a3569e7..57f70b2c0e 100644 --- a/frontend/src/app/shared/common.ts +++ b/frontend/src/app/shared/common.ts @@ -1,6 +1,5 @@ import { FormGroup } from '@angular/forms'; import { KeyResultMetricMin } from './types/model/KeyResultMetricMin'; -import { Quarter } from './types/model/Quarter'; export function getNumberOrNull(str: string | null | undefined): number | null { if (str === null || str === undefined || str.toString().trim() === '') { From c4333920ac8e061538ac2991391caf3dc271bb62 Mon Sep 17 00:00:00 2001 From: Miguel Lehmann Date: Mon, 28 Oct 2024 13:25:04 +0100 Subject: [PATCH 28/30] add dependency for hot reload in again --- backend/pom.xml | 9 +++++++++ .../keyresult-detail/keyresult-detail.component.html | 2 +- .../complete-dialog/complete-dialog.component.html | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/backend/pom.xml b/backend/pom.xml index 76954ba974..c08dbe7279 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -83,6 +83,15 @@ 3.26.3 test + + org.springframework.boot + spring-boot-devtools + + + org.springframework + springloaded + 1.2.8.RELEASE + diff --git a/frontend/src/app/components/keyresult-detail/keyresult-detail.component.html b/frontend/src/app/components/keyresult-detail/keyresult-detail.component.html index d020898f33..7bbecb71a6 100644 --- a/frontend/src/app/components/keyresult-detail/keyresult-detail.component.html +++ b/frontend/src/app/components/keyresult-detail/keyresult-detail.component.html @@ -41,7 +41,7 @@

{{ keyResult.title }}

*ngIf="keyResultMetric.lastCheckIn as lastCheckIn" class="keyResult-detail-attribute-show rounded-5 p-2 metric-col d-flex justify-content-center gap-1" [ngClass]="{ - 'border-error': calculateCurrentPercentage(keyResultMetric) < 1, + 'border-error': calculateCurrentPercentage(keyResultMetric) < 1 }" > ! diff --git a/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html b/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html index 5a0cb5d688..3919324c00 100644 --- a/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html +++ b/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html @@ -17,7 +17,7 @@ class="successful w-50 valuation-card card-hover-successful" [ngClass]="{ 'active-successful': completeForm.value.isSuccessful == true, - 'non-active-successful': completeForm.value.isSuccessful === false, + 'non-active-successful': completeForm.value.isSuccessful === false }" (click)="switchSuccessState('successful')" (keydown.enter)="switchSuccessState('successful')" @@ -30,7 +30,7 @@ class="not-successful w-50 valuation-card card-hover-not-successful" [ngClass]="{ 'active-not-successful': completeForm.value.isSuccessful === false, - 'non-active-not-successful': completeForm.value.isSuccessful == true, + 'non-active-not-successful': completeForm.value.isSuccessful == true }" (click)="switchSuccessState('notSuccessful')" (keydown.enter)="switchSuccessState('notSuccessful')" From 429d8d9141f79a43d7161a89cd83756eb0a723f8 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 28 Oct 2024 12:26:23 +0000 Subject: [PATCH 29/30] [FM] Automated formating frontend --- .../keyresult-detail/keyresult-detail.component.html | 2 +- .../dialog/complete-dialog/complete-dialog.component.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/keyresult-detail/keyresult-detail.component.html b/frontend/src/app/components/keyresult-detail/keyresult-detail.component.html index 7bbecb71a6..d020898f33 100644 --- a/frontend/src/app/components/keyresult-detail/keyresult-detail.component.html +++ b/frontend/src/app/components/keyresult-detail/keyresult-detail.component.html @@ -41,7 +41,7 @@

{{ keyResult.title }}

*ngIf="keyResultMetric.lastCheckIn as lastCheckIn" class="keyResult-detail-attribute-show rounded-5 p-2 metric-col d-flex justify-content-center gap-1" [ngClass]="{ - 'border-error': calculateCurrentPercentage(keyResultMetric) < 1 + 'border-error': calculateCurrentPercentage(keyResultMetric) < 1, }" > ! diff --git a/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html b/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html index 3919324c00..5a0cb5d688 100644 --- a/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html +++ b/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html @@ -17,7 +17,7 @@ class="successful w-50 valuation-card card-hover-successful" [ngClass]="{ 'active-successful': completeForm.value.isSuccessful == true, - 'non-active-successful': completeForm.value.isSuccessful === false + 'non-active-successful': completeForm.value.isSuccessful === false, }" (click)="switchSuccessState('successful')" (keydown.enter)="switchSuccessState('successful')" @@ -30,7 +30,7 @@ class="not-successful w-50 valuation-card card-hover-not-successful" [ngClass]="{ 'active-not-successful': completeForm.value.isSuccessful === false, - 'non-active-not-successful': completeForm.value.isSuccessful == true + 'non-active-not-successful': completeForm.value.isSuccessful == true, }" (click)="switchSuccessState('notSuccessful')" (keydown.enter)="switchSuccessState('notSuccessful')" From 2dbbd6236673e8237a973b9e1909794158c69f2b Mon Sep 17 00:00:00 2001 From: Yanick Minder Date: Mon, 4 Nov 2024 11:18:32 +0100 Subject: [PATCH 30/30] readd local debug run config --- .run/OkrApplication-local-prod-debug.run.xml | 17 +++++++ .../resources/application-staging.properties | 9 ++-- docker/dev-with-prod/docker-compose.yml | 46 ++++--------------- 3 files changed, 30 insertions(+), 42 deletions(-) create mode 100755 .run/OkrApplication-local-prod-debug.run.xml diff --git a/.run/OkrApplication-local-prod-debug.run.xml b/.run/OkrApplication-local-prod-debug.run.xml new file mode 100755 index 0000000000..15ae63f4ef --- /dev/null +++ b/.run/OkrApplication-local-prod-debug.run.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/backend/src/main/resources/application-staging.properties b/backend/src/main/resources/application-staging.properties index 2b162fbe66..31ce92a3c2 100644 --- a/backend/src/main/resources/application-staging.properties +++ b/backend/src/main/resources/application-staging.properties @@ -1,13 +1,10 @@ # logging level for staging logging.level.org.springframework=debug -logging.level.ch.puzzle.okr=DEBUG -#logging.level.org.flywaydb.core=DEBUG - connect.src=http://localhost:8544 http://localhost:8545 -hibernate.connection.url=jdbc:postgresql://localhost:5432/okr +hibernate.connection.url=jdbc:postgresql://okr-dev-db:5432/okr hibernate.connection.username=user hibernate.connection.password=pwd hibernate.multiTenancy=SCHEMA @@ -19,7 +16,7 @@ okr.datasource.driver-class-name=org.postgresql.Driver okr.user.champion.usernames=peggimann # pitc -okr.tenants.pitc.datasource.url=jdbc:postgresql://localhost:5432/okr +okr.tenants.pitc.datasource.url=jdbc:postgresql://okr-dev-db:5432/okr okr.tenants.pitc.datasource.username=user okr.tenants.pitc.datasource.password=pwd okr.tenants.pitc.datasource.schema=okr_pitc @@ -30,7 +27,7 @@ okr.tenants.pitc.security.oauth2.frontend.client-id=pitc_okr_staging # acme -okr.tenants.acme.datasource.url=jdbc:postgresql://localhost:5432/okr +okr.tenants.acme.datasource.url=jdbc:postgresql://okr-dev-db:5432/okr okr.tenants.acme.datasource.username=user okr.tenants.acme.datasource.password=pwd okr.tenants.acme.datasource.schema=okr_acme diff --git a/docker/dev-with-prod/docker-compose.yml b/docker/dev-with-prod/docker-compose.yml index f6ccdcee2c..db47c48efa 100644 --- a/docker/dev-with-prod/docker-compose.yml +++ b/docker/dev-with-prod/docker-compose.yml @@ -2,60 +2,34 @@ include: - ../docker-compose.yml services: spring: - tty: true container_name: spring build: context: . dockerfile: local-prod.Dockerfile restart: always environment: - SPRING_PROFILES_ACTIVE: dev + SPRING_PROFILES_ACTIVE: staging + LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: debug + SPRING_FLYWAY_LOCATION: classpath:db/migration,classpath:db/data-migration,classpath:db/callback volumes: - ../../../okr/backend/target:/app-root/backend network_mode: "host" - depends_on: - maven-init: - condition: service_completed_successfully maven: - tty: true container_name: maven - restart: on-failure image: maven:3.9.9-amazoncorretto-21 - command: mvn fizzed-watcher:run + command: sh -c "mvn fizzed-watcher:run" working_dir: /app-root/ volumes: - - ../../../okr:/app-root + - ../../../okr:/app-root/ - ~/.m2/repository:/root/.m2/repository - depends_on: - maven-init: - condition: service_completed_successfully - - maven-init: - tty: true - container_name: maven-init - image: maven:3.9.9-amazoncorretto-21 - command: mvn -B clean package -P build-for-docker,debug,no-formatter - working_dir: /app-root/ - volumes: - - ../../../okr:/app-root - - ~/.m2/repository:/root/.m2/repository - depends_on: - angular: - condition: service_healthy angular: container_name: angular - image: node:20 - tty: true - restart: on-failure + image: node:22 + user: "${UID:-1000}:${GID:-1000}" volumes: - ../../../okr:/opt - command: [ "/bin/bash", "-c", "cd /opt/frontend && rm -rf dist && npm ci && npm run watch:prod" ] - healthcheck: - test: bash -c "[ -f /opt/frontend/dist/frontend/index.html ]" - interval: 10s - retries: 999 - start_period: 30s - timeout: 10s - + - /etc/passwd:/etc/passwd:ro + - /etc/group:/etc/group:ro + command: [ "/bin/bash", "-c", "cd /opt/frontend && npm ci && npm run watch:prod" ]