From 92c64c2564e3042a772a7b967ea778975138e4d5 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 14 Mar 2026 13:12:25 +0100 Subject: [PATCH 01/11] 7. Wypushuj zmiany do repozytorium --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e707444aa..6ec00e6a8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ pl.wsb CapWSB-FitnessTracker - 1.2.0-SNAPSHOT + 1.2.0-81518-81477-SNAPSHOT org.springframework.boot spring-boot-starter-parent From 4b29da62d6533de88fc058c327794915f7a21a5e Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 14 Mar 2026 13:17:25 +0100 Subject: [PATCH 02/11] =?UTF-8?q?9.=20Zmie=C5=84=20tymczasowo=20port=20apl?= =?UTF-8?q?ikacji=20na=208091=20(w=20application.properties)=20i=20uruchom?= =?UTF-8?q?=20aplikacj=C4=99=20ponownie.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 0ee94079d..e4bdb0643 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,3 +1,4 @@ +server.port=8091 spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa From ef8a32ef48c2241d810c6cb3f5256ce1fdd626c6 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 14 Mar 2026 13:45:49 +0100 Subject: [PATCH 03/11] =?UTF-8?q?11.=20Zapoznaj=20si=C4=99=20z=20poni?= =?UTF-8?q?=C5=BCszym=20schematem=20relacyjnym=20bazy=20danych.=20Korzysta?= =?UTF-8?q?j=C4=85c=20z=20wiedzy=20przekazanej=20na=20wyk=C5=82adzie,=20li?= =?UTF-8?q?teratury=20=20=20=20=20oraz=20internetu=20zbuduj=20tabele=20Hea?= =?UTF-8?q?lthMetrcis.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../healthmetrics/api/HealthMetrics.java | 52 +++++++++++++++++++ .../healthmetrics/api/HealthMetricsDto.java | 15 ++++++ .../api/HealthMetricsNotFoundException.java | 16 ++++++ .../api/HealthMetricsProvider.java | 13 +++++ .../api/HealthMetricsService.java | 7 +++ 5 files changed, 103 insertions(+) create mode 100644 src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetrics.java create mode 100644 src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsDto.java create mode 100644 src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsNotFoundException.java create mode 100644 src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsProvider.java create mode 100644 src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsService.java diff --git a/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetrics.java b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetrics.java new file mode 100644 index 000000000..7c9b056fc --- /dev/null +++ b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetrics.java @@ -0,0 +1,52 @@ +package pl.wsb.fitnesstracker.healthmetrics.api; + +import jakarta.annotation.Nullable; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDate; + +@Entity +@Table(name = "health_metrics") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ToString +public class HealthMetrics { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Nullable + private Long id; + + @Column(name = "user_id", nullable = false) + private Long userId; + + @Column(name = "date", nullable = false) + private LocalDate date; + + @Column(name = "weight", nullable = false) + private Double weight; + + @Column(name = "height", nullable = false) + private Double height; + + @Column(name = "heart_rate", nullable = false) + private Integer heartRate; + + public HealthMetrics( + final Long userId, + final LocalDate date, + final Double weight, + final Double height, + final Integer heartRate) { + + this.userId = userId; + this.date = date; + this.weight = weight; + this.height = height; + this.heartRate = heartRate; + } +} diff --git a/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsDto.java b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsDto.java new file mode 100644 index 000000000..f51a78c67 --- /dev/null +++ b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsDto.java @@ -0,0 +1,15 @@ +package pl.wsb.fitnesstracker.healthmetrics.api; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.annotation.Nullable; + +import java.time.LocalDate; + +public record HealthMetricsDto( + @Nullable Long id, + Long userId, + @JsonFormat(pattern = "yyyy-MM-dd") LocalDate date, + Double weight, + Double height, + Integer heartRate) { +} diff --git a/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsNotFoundException.java b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsNotFoundException.java new file mode 100644 index 000000000..718426d6a --- /dev/null +++ b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsNotFoundException.java @@ -0,0 +1,16 @@ +package pl.wsb.fitnesstracker.healthmetrics.api; + +import pl.wsb.fitnesstracker.exception.api.NotFoundException; + +@SuppressWarnings("squid:S110") +public class HealthMetricsNotFoundException extends NotFoundException { + + private HealthMetricsNotFoundException(String message) { + super(message); + } + + public HealthMetricsNotFoundException(Long id) { + this("HealthMetrics with ID=%s was not found".formatted(id)); + } + +} diff --git a/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsProvider.java b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsProvider.java new file mode 100644 index 000000000..e816d22be --- /dev/null +++ b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsProvider.java @@ -0,0 +1,13 @@ +package pl.wsb.fitnesstracker.healthmetrics.api; + +import java.util.List; +import java.util.Optional; + +public interface HealthMetricsProvider { + Optional getHealthMetrics(Long healthMetricsId); + + List getHealthMetricsByUserId(Long userId); + + List findAllHealthMetrics(); + +} diff --git a/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsService.java b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsService.java new file mode 100644 index 000000000..e91f93731 --- /dev/null +++ b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetricsService.java @@ -0,0 +1,7 @@ +package pl.wsb.fitnesstracker.healthmetrics.api; + +public interface HealthMetricsService { + + HealthMetrics createHealthMetrics(HealthMetrics healthMetrics); + +} From f4d76b649942fc5d3273549379f628f4d02299fa Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 14 Mar 2026 13:46:26 +0100 Subject: [PATCH 04/11] -rm: package-info.java --- .../pl/wsb/fitnesstracker/healthmetrics/package-info.java | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/main/java/pl/wsb/fitnesstracker/healthmetrics/package-info.java diff --git a/src/main/java/pl/wsb/fitnesstracker/healthmetrics/package-info.java b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/package-info.java deleted file mode 100644 index 556ae2bf4..000000000 --- a/src/main/java/pl/wsb/fitnesstracker/healthmetrics/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NonNullByDefault -package pl.wsb.fitnesstracker.healthmetrics; - -import org.eclipse.jdt.annotation.NonNullByDefault; \ No newline at end of file From d94299cbe8b40991344b9c60e16e5d9d6f1e6842 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 14 Mar 2026 13:48:38 +0100 Subject: [PATCH 05/11] ?rev: db port --- README.md | 4 ++++ src/main/resources/application.properties | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..af2bb1609 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# FitnessTracker_LM-SS + +Lukasz Malinowski 81518 +Sebastian Skowron 81477 diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e4bdb0643..51f229dbf 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,4 +1,4 @@ -server.port=8091 +#server.port=8091 spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa From 1b0306b5f48452d3fd73e583084c85122523273b Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 14 Mar 2026 13:53:47 +0100 Subject: [PATCH 06/11] +adj: README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index af2bb1609..5c387551c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # FitnessTracker_LM-SS Lukasz Malinowski 81518 +
Sebastian Skowron 81477 From 9c05daae2e8dd309210482fe1280e02e65067fa2 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 28 Mar 2026 13:01:00 +0100 Subject: [PATCH 07/11] 2 --- .github/workflows/github-ci-cd.yml | 53 ++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/github-ci-cd.yml diff --git a/.github/workflows/github-ci-cd.yml b/.github/workflows/github-ci-cd.yml new file mode 100644 index 000000000..af0b0753d --- /dev/null +++ b/.github/workflows/github-ci-cd.yml @@ -0,0 +1,53 @@ +# GitHub Actions CI/CD pipeline for a Java project using Maven +# File path should be: .github/workflows/github-ci-cd.yml +name: Java CI with Maven +permissions: + contents: read + security-events: write +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + id: setup-jdk + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Build with Maven + run: mvn -B package --file pom.xml + publish-job: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'temurin' + - run: mvn --batch-mode --update-snapshots verify + - name: Prepare staging + id: prepare-staging + run: | + mkdir -p staging + find target -maxdepth 1 -type f -name '*.jar' ! -name '*SNAPSHOT*' -exec cp {} staging/ \; + if [ -n "$(ls -A staging 2>/dev/null)" ]; then + echo "found=true" >> $GITHUB_OUTPUT + else + echo "found=false" >> $GITHUB_OUTPUT + fi + - name: Upload package + if: steps.prepare-staging.outputs.found == 'true' + uses: actions/upload-artifact@v5 + with: + name: Package + path: staging \ No newline at end of file From fc49611f455116d90acf14257361a8122e198ef9 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 28 Mar 2026 13:40:23 +0100 Subject: [PATCH 08/11] all-in-one lab3 --- .../healthmetrics/api/HealthMetrics.java | 11 +-- .../statistics/api/Statistics.java | 15 +++- .../fitnesstracker/training/api/Training.java | 22 +++++- .../pl/wsb/fitnesstracker/user/api/User.java | 12 ++- .../application-loadInitialData.properties | 3 + src/main/resources/application.properties | 1 + src/main/resources/data.sql | 77 +++++++++++++++++++ .../fitnesstracker}/DatabaseSchemaTest.java | 0 8 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 src/main/resources/application-loadInitialData.properties create mode 100644 src/main/resources/data.sql rename src/{main/resources/JPA/LAB02 => test/java/pl/wsb/fitnesstracker}/DatabaseSchemaTest.java (100%) diff --git a/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetrics.java b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetrics.java index 7c9b056fc..427c0f282 100644 --- a/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetrics.java +++ b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/api/HealthMetrics.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; +import pl.wsb.fitnesstracker.user.api.User; import java.time.LocalDate; @@ -21,8 +22,9 @@ public class HealthMetrics { @Nullable private Long id; - @Column(name = "user_id", nullable = false) - private Long userId; + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User user; @Column(name = "date", nullable = false) private LocalDate date; @@ -37,13 +39,12 @@ public class HealthMetrics { private Integer heartRate; public HealthMetrics( - final Long userId, + final User user, final LocalDate date, final Double weight, final Double height, final Integer heartRate) { - - this.userId = userId; + this.user = user; this.date = date; this.weight = weight; this.height = height; diff --git a/src/main/java/pl/wsb/fitnesstracker/statistics/api/Statistics.java b/src/main/java/pl/wsb/fitnesstracker/statistics/api/Statistics.java index 0b277c64d..8b54a4723 100644 --- a/src/main/java/pl/wsb/fitnesstracker/statistics/api/Statistics.java +++ b/src/main/java/pl/wsb/fitnesstracker/statistics/api/Statistics.java @@ -6,9 +6,10 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; +import pl.wsb.fitnesstracker.user.api.User; @Entity -@Table(name = "Statistics") +@Table(name = "statistics") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @ToString @@ -19,15 +20,23 @@ public class Statistics { @Nullable private Long id; + @OneToOne + @JoinColumn(name = "user_id", nullable = false, unique = true) + private User user; + + @Column(name = "total_trainings", nullable = false) private int totalTrainings; + @Column(name = "total_distance", nullable = false) private double totalDistance; + @Column(name = "total_calories_burned", nullable = false) private int totalCaloriesBurned; - public Statistics(int totalTrainings, double totalDistance, int totalCaloriesBurned) { + public Statistics(User user, int totalTrainings, double totalDistance, int totalCaloriesBurned) { + this.user = user; this.totalTrainings = totalTrainings; this.totalDistance = totalDistance; this.totalCaloriesBurned = totalCaloriesBurned; } -} \ No newline at end of file +} diff --git a/src/main/java/pl/wsb/fitnesstracker/training/api/Training.java b/src/main/java/pl/wsb/fitnesstracker/training/api/Training.java index 03c8362b0..6f0c87f30 100644 --- a/src/main/java/pl/wsb/fitnesstracker/training/api/Training.java +++ b/src/main/java/pl/wsb/fitnesstracker/training/api/Training.java @@ -1,26 +1,46 @@ package pl.wsb.fitnesstracker.training.api; +import jakarta.annotation.Nullable; +import jakarta.persistence.*; +import lombok.AccessLevel; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; import pl.wsb.fitnesstracker.training.internal.ActivityType; import pl.wsb.fitnesstracker.user.api.User; import java.util.Date; +@Entity +@Table(name = "trainings") @Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ToString public class Training { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Nullable private Long id; + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) private User user; + @Column(name = "start_time", nullable = false) private Date startTime; + @Column(name = "end_time", nullable = false) private Date endTime; + @Enumerated(EnumType.STRING) + @Column(name = "activity_type", nullable = false) private ActivityType activityType; + @Column(name = "distance") private double distance; + @Column(name = "average_speed") private double averageSpeed; public Training( @@ -37,4 +57,4 @@ public Training( this.distance = distance; this.averageSpeed = averageSpeed; } -} \ No newline at end of file +} diff --git a/src/main/java/pl/wsb/fitnesstracker/user/api/User.java b/src/main/java/pl/wsb/fitnesstracker/user/api/User.java index df37a5a0e..fefaf029a 100644 --- a/src/main/java/pl/wsb/fitnesstracker/user/api/User.java +++ b/src/main/java/pl/wsb/fitnesstracker/user/api/User.java @@ -21,7 +21,13 @@ public class User { @Nullable private Long id; - @Column(name = "birthdate", nullable = false) + @Column(name = "first_name", nullable = false) + private String firstName; + + @Column(name = "last_name", nullable = false) + private String lastName; + + @Column(name = "birthday", nullable = false) private LocalDate birthdate; @Column(nullable = false, unique = true) @@ -32,10 +38,10 @@ public User( final String lastName, final LocalDate birthdate, final String email) { - + this.firstName = firstName; + this.lastName = lastName; this.birthdate = birthdate; this.email = email; } } - diff --git a/src/main/resources/application-loadInitialData.properties b/src/main/resources/application-loadInitialData.properties new file mode 100644 index 000000000..a8b3546b5 --- /dev/null +++ b/src/main/resources/application-loadInitialData.properties @@ -0,0 +1,3 @@ +# Profile for loading initial sample data on startup +# Activates InitialDataLoader component +spring.jpa.defer-datasource-initialization=true diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 51f229dbf..c0a984f3d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -18,3 +18,4 @@ logging.level.org.springframework.orm.jpa=INFO # spring.datasource.hikari.schema=fitnesstracker # spring.jpa.properties.hibernate.default_schema=fitnesstracker spring.sql.init.mode=always +spring.jpa.defer-datasource-initialization=true diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 000000000..db3856fd4 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,77 @@ +-- ============================================= +-- Sample data for FitnessTracker +-- Insert order respects foreign key constraints +-- ============================================= + +-- 1. Users (no dependencies) +INSERT INTO users (first_name, last_name, birthday, email) +VALUES ('Jan', 'Kowalski', '1995-06-15', 'jan.kowalski@example.com'); + +INSERT INTO users (first_name, last_name, birthday, email) +VALUES ('Anna', 'Nowak', '1988-03-22', 'anna.nowak@example.com'); + +INSERT INTO users (first_name, last_name, birthday, email) +VALUES ('Piotr', 'Wisniewski', '2000-11-08', 'piotr.wisniewski@example.com'); + +INSERT INTO users (first_name, last_name, birthday, email) +VALUES ('Maria', 'Zielinska', '1992-09-30', 'maria.zielinska@example.com'); + +INSERT INTO users (first_name, last_name, birthday, email) +VALUES ('Tomasz', 'Lewandowski', '1985-01-17', 'tomasz.lewandowski@example.com'); + +-- 2. Trainings (depends on users) +INSERT INTO trainings (user_id, start_time, end_time, activity_type, distance, average_speed) +VALUES (1, '2024-03-01 07:00:00', '2024-03-01 08:15:00', 'RUNNING', 10.5, 8.4); + +INSERT INTO trainings (user_id, start_time, end_time, activity_type, distance, average_speed) +VALUES (1, '2024-03-03 17:00:00', '2024-03-03 18:30:00', 'CYCLING', 25.0, 16.7); + +INSERT INTO trainings (user_id, start_time, end_time, activity_type, distance, average_speed) +VALUES (2, '2024-03-02 06:30:00', '2024-03-02 07:45:00', 'SWIMMING', 2.0, 1.6); + +INSERT INTO trainings (user_id, start_time, end_time, activity_type, distance, average_speed) +VALUES (3, '2024-03-04 09:00:00', '2024-03-04 10:00:00', 'WALKING', 5.2, 5.2); + +INSERT INTO trainings (user_id, start_time, end_time, activity_type, distance, average_speed) +VALUES (4, '2024-03-05 18:00:00', '2024-03-05 19:30:00', 'TENNIS', 0.0, 0.0); + +INSERT INTO trainings (user_id, start_time, end_time, activity_type, distance, average_speed) +VALUES (5, '2024-03-06 08:00:00', '2024-03-06 09:00:00', 'RUNNING', 8.3, 8.3); + +INSERT INTO trainings (user_id, start_time, end_time, activity_type, distance, average_speed) +VALUES (2, '2024-03-07 16:00:00', '2024-03-07 17:30:00', 'CYCLING', 30.0, 20.0); + +-- 3. Statistics (depends on users, OneToOne) +INSERT INTO statistics (user_id, total_trainings, total_distance, total_calories_burned) +VALUES (1, 15, 120.5, 8500); + +INSERT INTO statistics (user_id, total_trainings, total_distance, total_calories_burned) +VALUES (2, 22, 85.3, 6200); + +INSERT INTO statistics (user_id, total_trainings, total_distance, total_calories_burned) +VALUES (3, 8, 42.0, 2800); + +INSERT INTO statistics (user_id, total_trainings, total_distance, total_calories_burned) +VALUES (4, 10, 0.0, 4500); + +INSERT INTO statistics (user_id, total_trainings, total_distance, total_calories_burned) +VALUES (5, 12, 95.7, 7100); + +-- 4. Health Metrics (depends on users, ManyToOne) +INSERT INTO health_metrics (user_id, date, weight, height, heart_rate) +VALUES (1, '2024-03-01', 78.5, 180.0, 65); + +INSERT INTO health_metrics (user_id, date, weight, height, heart_rate) +VALUES (1, '2024-03-15', 77.8, 180.0, 63); + +INSERT INTO health_metrics (user_id, date, weight, height, heart_rate) +VALUES (2, '2024-03-01', 62.0, 165.0, 70); + +INSERT INTO health_metrics (user_id, date, weight, height, heart_rate) +VALUES (3, '2024-03-01', 85.2, 175.0, 72); + +INSERT INTO health_metrics (user_id, date, weight, height, heart_rate) +VALUES (4, '2024-03-01', 58.0, 168.0, 68); + +INSERT INTO health_metrics (user_id, date, weight, height, heart_rate) +VALUES (5, '2024-03-01', 90.0, 185.0, 60); diff --git a/src/main/resources/JPA/LAB02/DatabaseSchemaTest.java b/src/test/java/pl/wsb/fitnesstracker/DatabaseSchemaTest.java similarity index 100% rename from src/main/resources/JPA/LAB02/DatabaseSchemaTest.java rename to src/test/java/pl/wsb/fitnesstracker/DatabaseSchemaTest.java From a62b9fe337bb6cbe83fa25cfccaec42ac712dbd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Malinowski?= Date: Sat, 9 May 2026 13:15:22 +0200 Subject: [PATCH 09/11] Pushowanie zmian Lab03. --- .claude/settings.local.json | 8 ++ .github/workflows/github-ci-cd.yml | 102 +++++++++++---- .gitignore | 5 +- .../pl/wsb/fitnesstracker/event/Event.java | 53 +++++++- .../fitnesstracker/event/EventRepository.java | 14 +++ .../healthmetrics/HealthMetrics.java | 48 ------- .../training/internal/TrainingRepository.java | 8 ++ .../fitnesstracker/userevent/UserEvent.java | 43 +++++++ .../workoutsession/WorkoutSession.java | 56 ++++++++- src/main/resources/data.sql | 10 +- .../wsb/fitnesstracker/Lab03EntitiesTest.java | 118 ++++++++++++++++++ 11 files changed, 378 insertions(+), 87 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 src/main/java/pl/wsb/fitnesstracker/event/EventRepository.java delete mode 100644 src/main/java/pl/wsb/fitnesstracker/healthmetrics/HealthMetrics.java create mode 100644 src/main/java/pl/wsb/fitnesstracker/userevent/UserEvent.java create mode 100644 src/test/java/pl/wsb/fitnesstracker/Lab03EntitiesTest.java diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 000000000..13e9b5ac6 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "Bash(git checkout:*)", + "Bash(mvn -B spring-boot:run)" + ] + } +} diff --git a/.github/workflows/github-ci-cd.yml b/.github/workflows/github-ci-cd.yml index af0b0753d..1526921fd 100644 --- a/.github/workflows/github-ci-cd.yml +++ b/.github/workflows/github-ci-cd.yml @@ -1,53 +1,101 @@ # GitHub Actions CI/CD pipeline for a Java project using Maven # File path should be: .github/workflows/github-ci-cd.yml +# +# Testy podzielone są na stage'e zgodnie z laboratoriami: +# - lab02 -> DatabaseSchemaTest (encje z LAB02) +# - lab03-entities -> Lab03EntitiesTest (encje z LAB03, STAGE 1) +# +# Stage'e testowe są niezależne (brak `needs:` między nimi), więc niepowodzenie +# jednego nie przerywa pozostałych — widzisz pełny obraz, które laboratorium +# już zielenie się, a które jeszcze nie. Job `package` buduje JAR tylko, +# gdy wszystkie stage'e są zielone. name: Java CI with Maven + permissions: contents: read - security-events: write + on: push: - branches: [ "master" ] + branches: [ "**" ] pull_request: - branches: [ "master" ] + branches: [ "**" ] jobs: - build: + lab02: + name: LAB02 — schema (DatabaseSchemaTest) runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up JDK 17 + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Run LAB02 tests + run: mvn -B test -Dtest=DatabaseSchemaTest -DfailIfNoTests=false --file pom.xml + + - name: Publish LAB02 test results + if: always() + uses: actions/upload-artifact@v5 + with: + name: surefire-lab02 + path: target/surefire-reports/ + if-no-files-found: ignore + retention-days: 7 + lab03-entities: + name: LAB03-1 — entities (Lab03EntitiesTest) + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v5 + - name: Set up JDK 17 - id: setup-jdk uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' cache: maven - - name: Build with Maven - run: mvn -B package --file pom.xml - publish-job: + + - name: Run LAB03-1 tests + run: mvn -B test -Dtest=Lab03EntitiesTest -DfailIfNoTests=false --file pom.xml + + - name: Publish LAB03-1 test results + if: always() + uses: actions/upload-artifact@v5 + with: + name: surefire-lab03-entities + path: target/surefire-reports/ + if-no-files-found: ignore + retention-days: 7 + + package: + name: Package JAR runs-on: ubuntu-latest - needs: build + needs: [ lab02, lab03-entities ] steps: - - uses: actions/checkout@v5 - - uses: actions/setup-java@v5 + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' - - run: mvn --batch-mode --update-snapshots verify - - name: Prepare staging - id: prepare-staging - run: | - mkdir -p staging - find target -maxdepth 1 -type f -name '*.jar' ! -name '*SNAPSHOT*' -exec cp {} staging/ \; - if [ -n "$(ls -A staging 2>/dev/null)" ]; then - echo "found=true" >> $GITHUB_OUTPUT - else - echo "found=false" >> $GITHUB_OUTPUT - fi - - name: Upload package - if: steps.prepare-staging.outputs.found == 'true' + cache: maven + + - name: Build JAR + run: mvn -B -DskipTests package --file pom.xml + + - name: Upload JAR artifact + if: success() uses: actions/upload-artifact@v5 with: - name: Package - path: staging \ No newline at end of file + name: package + path: target/*.jar + if-no-files-found: ignore + retention-days: 7 diff --git a/.gitignore b/.gitignore index f4ffc6f5f..e3c93ad6e 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,7 @@ build/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store + +### Java ### +target/ diff --git a/src/main/java/pl/wsb/fitnesstracker/event/Event.java b/src/main/java/pl/wsb/fitnesstracker/event/Event.java index 5d1dc4feb..7320fb2c2 100644 --- a/src/main/java/pl/wsb/fitnesstracker/event/Event.java +++ b/src/main/java/pl/wsb/fitnesstracker/event/Event.java @@ -1,5 +1,56 @@ package pl.wsb.fitnesstracker.event; -// TODO: Define the Event entity with appropriate fields and annotations +import jakarta.annotation.Nullable; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "event") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ToString public class Event { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Nullable + private Long id; + + @Column(nullable = false) + private String name; + + @Column + private String description; + + @Column(name = "start_time", nullable = false) + private LocalDateTime startTime; + + @Column(name = "end_time") + private LocalDateTime endTime; + + @Column + private String country; + + @Column + private String city; + + public Event( + final String name, + final String description, + final LocalDateTime startTime, + final LocalDateTime endTime, + final String country, + final String city) { + this.name = name; + this.description = description; + this.startTime = startTime; + this.endTime = endTime; + this.country = country; + this.city = city; + } } diff --git a/src/main/java/pl/wsb/fitnesstracker/event/EventRepository.java b/src/main/java/pl/wsb/fitnesstracker/event/EventRepository.java new file mode 100644 index 000000000..85ddd6e4b --- /dev/null +++ b/src/main/java/pl/wsb/fitnesstracker/event/EventRepository.java @@ -0,0 +1,14 @@ +package pl.wsb.fitnesstracker.event; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.time.LocalDateTime; +import java.util.List; + +public interface EventRepository extends JpaRepository { + + @Query("SELECT e FROM Event e WHERE e.startTime > :now ORDER BY e.startTime") + List findUpcoming(@Param("now") LocalDateTime now); +} diff --git a/src/main/java/pl/wsb/fitnesstracker/healthmetrics/HealthMetrics.java b/src/main/java/pl/wsb/fitnesstracker/healthmetrics/HealthMetrics.java deleted file mode 100644 index b038bc3d1..000000000 --- a/src/main/java/pl/wsb/fitnesstracker/healthmetrics/HealthMetrics.java +++ /dev/null @@ -1,48 +0,0 @@ -package pl.wsb.fitnesstracker.healthmetrics; - -import jakarta.annotation.Nullable; -import jakarta.persistence.*; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; -import pl.wsb.fitnesstracker.user.api.User; - -import java.time.LocalDate; - -@Entity -@Table(name = "health_metrics") -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@ToString -public class HealthMetrics { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Nullable - private Long id; - - @ManyToOne - @JoinColumn(name = "user_id", nullable = false) - private User user; - - @Column(nullable = false) - private LocalDate date; - - @Column(nullable = false) - private double weight; - - @Column(nullable = false) - private double height; - - @Column(name = "heart_rate", nullable = false) - private int heartRate; - - public HealthMetrics(User user, LocalDate date, double weight, double height, int heartRate) { - this.user = user; - this.date = date; - this.weight = weight; - this.height = height; - this.heartRate = heartRate; - } -} diff --git a/src/main/java/pl/wsb/fitnesstracker/training/internal/TrainingRepository.java b/src/main/java/pl/wsb/fitnesstracker/training/internal/TrainingRepository.java index 53b4a2bff..d0f87e591 100644 --- a/src/main/java/pl/wsb/fitnesstracker/training/internal/TrainingRepository.java +++ b/src/main/java/pl/wsb/fitnesstracker/training/internal/TrainingRepository.java @@ -1,7 +1,15 @@ package pl.wsb.fitnesstracker.training.internal; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import pl.wsb.fitnesstracker.training.api.Training; public interface TrainingRepository extends JpaRepository { + + @Query( + value = "SELECT COALESCE(SUM(distance), 0) FROM trainings WHERE user_id = :userId", + nativeQuery = true + ) + double sumDistanceByUserId(@Param("userId") Long userId); } diff --git a/src/main/java/pl/wsb/fitnesstracker/userevent/UserEvent.java b/src/main/java/pl/wsb/fitnesstracker/userevent/UserEvent.java new file mode 100644 index 000000000..76545287e --- /dev/null +++ b/src/main/java/pl/wsb/fitnesstracker/userevent/UserEvent.java @@ -0,0 +1,43 @@ +package pl.wsb.fitnesstracker.userevent; + +import jakarta.annotation.Nullable; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import pl.wsb.fitnesstracker.event.Event; +import pl.wsb.fitnesstracker.user.api.User; + +@Entity +@Table(name = "user_event") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ToString +public class UserEvent { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Nullable + private Long id; + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @ManyToOne + @JoinColumn(name = "event_id", nullable = false) + private Event event; + + @Column + private String status; + + public UserEvent( + final User user, + final Event event, + final String status) { + this.user = user; + this.event = event; + this.status = status; + } +} diff --git a/src/main/java/pl/wsb/fitnesstracker/workoutsession/WorkoutSession.java b/src/main/java/pl/wsb/fitnesstracker/workoutsession/WorkoutSession.java index e2f70f78c..c54df055d 100644 --- a/src/main/java/pl/wsb/fitnesstracker/workoutsession/WorkoutSession.java +++ b/src/main/java/pl/wsb/fitnesstracker/workoutsession/WorkoutSession.java @@ -1,17 +1,63 @@ package pl.wsb.fitnesstracker.workoutsession; -import jakarta.persistence.Id; +import jakarta.annotation.Nullable; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import pl.wsb.fitnesstracker.training.api.Training; -// TODO: Define the Event entity with appropriate fields and annotations +import java.time.LocalDateTime; + +@Entity +@Table(name = "workout_session") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ToString public class WorkoutSession { @Id - private int id; - private int trainingId; - private String timestamp; + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Nullable + private Long id; + + @ManyToOne + @JoinColumn(name = "training_id", nullable = false) + private Training training; + + @Column(nullable = false) + private LocalDateTime timestamp; + + @Column(name = "start_latitude", nullable = false) private double startLatitude; + + @Column(name = "start_longitude", nullable = false) private double startLongitude; + + @Column(name = "end_latitude", nullable = false) private double endLatitude; + + @Column(name = "end_longitude", nullable = false) private double endLongitude; + + @Column(nullable = false) private double altitude; + + public WorkoutSession( + final Training training, + final LocalDateTime timestamp, + final double startLatitude, + final double startLongitude, + final double endLatitude, + final double endLongitude, + final double altitude) { + this.training = training; + this.timestamp = timestamp; + this.startLatitude = startLatitude; + this.startLongitude = startLongitude; + this.endLatitude = endLatitude; + this.endLongitude = endLongitude; + this.altitude = altitude; + } } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index db3856fd4..0389eed60 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -4,19 +4,19 @@ -- ============================================= -- 1. Users (no dependencies) -INSERT INTO users (first_name, last_name, birthday, email) +INSERT INTO users (first_name, last_name, birthdate, email) VALUES ('Jan', 'Kowalski', '1995-06-15', 'jan.kowalski@example.com'); -INSERT INTO users (first_name, last_name, birthday, email) +INSERT INTO users (first_name, last_name, birthdate, email) VALUES ('Anna', 'Nowak', '1988-03-22', 'anna.nowak@example.com'); -INSERT INTO users (first_name, last_name, birthday, email) +INSERT INTO users (first_name, last_name, birthdate, email) VALUES ('Piotr', 'Wisniewski', '2000-11-08', 'piotr.wisniewski@example.com'); -INSERT INTO users (first_name, last_name, birthday, email) +INSERT INTO users (first_name, last_name, birthdate, email) VALUES ('Maria', 'Zielinska', '1992-09-30', 'maria.zielinska@example.com'); -INSERT INTO users (first_name, last_name, birthday, email) +INSERT INTO users (first_name, last_name, birthdate, email) VALUES ('Tomasz', 'Lewandowski', '1985-01-17', 'tomasz.lewandowski@example.com'); -- 2. Trainings (depends on users) diff --git a/src/test/java/pl/wsb/fitnesstracker/Lab03EntitiesTest.java b/src/test/java/pl/wsb/fitnesstracker/Lab03EntitiesTest.java new file mode 100644 index 000000000..5e368e300 --- /dev/null +++ b/src/test/java/pl/wsb/fitnesstracker/Lab03EntitiesTest.java @@ -0,0 +1,118 @@ +package pl.wsb.fitnesstracker; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * LAB03 — STAGE 1: encje. + * + * Ten plik sprawdza tylko istnienie i strukturę tabel dla nowych encji LAB03 + * (Event, UserEvent, WorkoutSession). NIE używa żadnego repozytorium — zieleni + * się wyłącznie na podstawie poprawnych encji JPA. + * + * Class should be under src/test/java/pl/wsb/fitnesstracker. + * + * Wymagane nazwy tabel (np. przez @Table(name = "...")): + * - event + * - user_event (z kolumnami user_id, event_id) + * - workout_session (z kolumną training_id) + */ +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY) +class Lab03EntitiesTest { + + @Autowired + private DataSource dataSource; + + @Test + void shouldHaveEventTable() throws Exception { + try (Connection conn = dataSource.getConnection()) { + assertThat(tableExists(conn, "event")).isTrue(); + } + } + + @Test + void shouldHaveUserEventTable() throws Exception { + try (Connection conn = dataSource.getConnection()) { + assertThat(tableExists(conn, "user_event")).isTrue(); + } + } + + @Test + void shouldHaveWorkoutSessionTable() throws Exception { + try (Connection conn = dataSource.getConnection()) { + assertThat(tableExists(conn, "workout_session")).isTrue(); + } + } + + @Test + void eventTableHasPrimaryKey() throws Exception { + try (Connection conn = dataSource.getConnection()) { + Set cols = tableColumns(conn, "event"); + assertThat(cols).contains("id"); + } + } + + @Test + void userEventTableHasForeignKeyColumns() throws Exception { + try (Connection conn = dataSource.getConnection()) { + Set cols = tableColumns(conn, "user_event"); + assertThat(cols).contains("id", "user_id", "event_id"); + } + } + + @Test + void workoutSessionTableHasTrainingForeignKey() throws Exception { + try (Connection conn = dataSource.getConnection()) { + Set cols = tableColumns(conn, "workout_session"); + assertThat(cols).contains("id", "training_id"); + } + } + + private boolean tableExists(Connection conn, String expectedName) throws SQLException { + DatabaseMetaData meta = conn.getMetaData(); + try (ResultSet rs = meta.getTables(conn.getCatalog(), null, "%", new String[]{"TABLE"})) { + while (rs.next()) { + String schema = rs.getString("TABLE_SCHEM"); + if (schema == null) continue; + if (!"PUBLIC".equalsIgnoreCase(schema)) continue; + String name = rs.getString("TABLE_NAME"); + if (expectedName.equalsIgnoreCase(name)) { + return true; + } + } + } + return false; + } + + private Set tableColumns(Connection conn, String tableName) throws SQLException { + DatabaseMetaData meta = conn.getMetaData(); + Set cols = new HashSet<>(); + try (ResultSet rs = meta.getColumns(conn.getCatalog(), null, "%", "%")) { + while (rs.next()) { + String schema = rs.getString("TABLE_SCHEM"); + if (schema == null) continue; + if (!"PUBLIC".equalsIgnoreCase(schema)) continue; + String tbl = rs.getString("TABLE_NAME"); + if (!tableName.equalsIgnoreCase(tbl)) continue; + String col = rs.getString("COLUMN_NAME"); + if (col != null) { + cols.add(col.toLowerCase()); + } + } + } + return cols; + } +} From 7a40d803434174e98e870ee5f762d62f4e391d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Malinowski?= Date: Fri, 29 May 2026 22:14:04 +0200 Subject: [PATCH 10/11] Pushowanie zmian Lab03. --- .../achievement/Achievement.java | 48 +++++++++++++++++++ .../JPA/LAB03/Lab03RepositoryTest.java | 20 -------- .../wsb/fitnesstracker/Lab03EntitiesTest.java | 29 +++++------ 3 files changed, 63 insertions(+), 34 deletions(-) create mode 100644 src/main/java/pl/wsb/fitnesstracker/achievement/Achievement.java diff --git a/src/main/java/pl/wsb/fitnesstracker/achievement/Achievement.java b/src/main/java/pl/wsb/fitnesstracker/achievement/Achievement.java new file mode 100644 index 000000000..2fa31bd87 --- /dev/null +++ b/src/main/java/pl/wsb/fitnesstracker/achievement/Achievement.java @@ -0,0 +1,48 @@ +package pl.wsb.fitnesstracker.achievement; + +import jakarta.persistence.*; +import pl.wsb.fitnesstracker.user.api.User; +import java.time.LocalDateTime; + +@Entity +@Table(name = "achievement") +public class Achievement { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + @Column(name = "earned_at") + private LocalDateTime earnedAt; + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User user; + + protected Achievement() { + } + + public Achievement(String name, LocalDateTime earnedAt, User user) { + this.name = name; + this.earnedAt = earnedAt; + this.user = user; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public LocalDateTime getEarnedAt() { + return earnedAt; + } + + public User getUser() { + return user; + } +} \ No newline at end of file diff --git a/src/main/resources/JPA/LAB03/Lab03RepositoryTest.java b/src/main/resources/JPA/LAB03/Lab03RepositoryTest.java index e6fe6ca81..9ad41eae1 100644 --- a/src/main/resources/JPA/LAB03/Lab03RepositoryTest.java +++ b/src/main/resources/JPA/LAB03/Lab03RepositoryTest.java @@ -17,26 +17,6 @@ import static org.assertj.core.api.Assertions.assertThat; -/** - * LAB03 — STAGE 2: repozytoria i zapytania. - * - * Ten plik sprawdza, że napisałeś @Query z sekcji 3 i 4 LAB03. Aby ten plik - * w ogóle się skompilował, musisz najpierw stworzyć (mogą być puste szkielety): - * - pl.wsb.fitnesstracker.event.Event - * - pl.wsb.fitnesstracker.event.UserEvent - * - pl.wsb.fitnesstracker.event.EventRepository (extends JpaRepository) - * - pl.wsb.fitnesstracker.event.UserEventRepository (extends JpaRepository) - * - * Wymagane konstruktory: - * - new Event(String name, LocalDate startDate, String location) - * - new UserEvent(User user, Event event, LocalDate registrationDate) - * - * Wymagane metody w repozytoriach: - * - EventRepository: List findUpcoming(LocalDate now) (JPQL) - * - UserEventRepository: long countParticipants(Long eventId) (nativeQuery = true) - * - * Class should be under src/test/java/pl/wsb/fitnesstracker. - */ @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY) class Lab03RepositoryTest { diff --git a/src/test/java/pl/wsb/fitnesstracker/Lab03EntitiesTest.java b/src/test/java/pl/wsb/fitnesstracker/Lab03EntitiesTest.java index 5e368e300..66ab4988a 100644 --- a/src/test/java/pl/wsb/fitnesstracker/Lab03EntitiesTest.java +++ b/src/test/java/pl/wsb/fitnesstracker/Lab03EntitiesTest.java @@ -15,20 +15,6 @@ import static org.assertj.core.api.Assertions.assertThat; -/** - * LAB03 — STAGE 1: encje. - * - * Ten plik sprawdza tylko istnienie i strukturę tabel dla nowych encji LAB03 - * (Event, UserEvent, WorkoutSession). NIE używa żadnego repozytorium — zieleni - * się wyłącznie na podstawie poprawnych encji JPA. - * - * Class should be under src/test/java/pl/wsb/fitnesstracker. - * - * Wymagane nazwy tabel (np. przez @Table(name = "...")): - * - event - * - user_event (z kolumnami user_id, event_id) - * - workout_session (z kolumną training_id) - */ @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY) class Lab03EntitiesTest { @@ -81,6 +67,21 @@ void workoutSessionTableHasTrainingForeignKey() throws Exception { } } + @Test + void shouldHaveAchievementTable() throws Exception { + try (Connection conn = dataSource.getConnection()) { + assertThat(tableExists(conn, "achievement")).isTrue(); + } + } + + @Test + void achievementTableHasUserForeignKey() throws Exception { + try (Connection conn = dataSource.getConnection()) { + Set cols = tableColumns(conn, "achievement"); + assertThat(cols).contains("id", "user_id"); + } + } + private boolean tableExists(Connection conn, String expectedName) throws SQLException { DatabaseMetaData meta = conn.getMetaData(); try (ResultSet rs = meta.getTables(conn.getCatalog(), null, "%", new String[]{"TABLE"})) { From a39edb983bc63d26f7b9ab96c82811a9c4527810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Malinowski?= Date: Fri, 29 May 2026 23:52:12 +0200 Subject: [PATCH 11/11] Gotowy Lab03 - encje i repozytoria --- pom.xml | 12 ++++++++++++ .../pl/wsb/fitnesstracker/event/EventRepository.java | 7 +++++++ .../java/pl/wsb/fitnesstracker/user/api/User.java | 8 ++++++++ .../fitnesstracker/user/internal/UserController.java | 7 ++++++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6ec00e6a8..11efbcd6e 100644 --- a/pom.xml +++ b/pom.xml @@ -101,6 +101,18 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + 1.18.32 + + + \ No newline at end of file diff --git a/src/main/java/pl/wsb/fitnesstracker/event/EventRepository.java b/src/main/java/pl/wsb/fitnesstracker/event/EventRepository.java index 85ddd6e4b..d616396f6 100644 --- a/src/main/java/pl/wsb/fitnesstracker/event/EventRepository.java +++ b/src/main/java/pl/wsb/fitnesstracker/event/EventRepository.java @@ -11,4 +11,11 @@ public interface EventRepository extends JpaRepository { @Query("SELECT e FROM Event e WHERE e.startTime > :now ORDER BY e.startTime") List findUpcoming(@Param("now") LocalDateTime now); + + @Query( + value = "SELECT e.name, COUNT(ue.id) FROM event e LEFT JOIN user_event ue ON e.id = ue.event_id GROUP BY e.id, e.name", + nativeQuery = true + ) + + List getEventsWithParticipantCount(); } diff --git a/src/main/java/pl/wsb/fitnesstracker/user/api/User.java b/src/main/java/pl/wsb/fitnesstracker/user/api/User.java index 844ae110d..ff8fe3cea 100644 --- a/src/main/java/pl/wsb/fitnesstracker/user/api/User.java +++ b/src/main/java/pl/wsb/fitnesstracker/user/api/User.java @@ -43,5 +43,13 @@ public User( this.birthdate = birthdate; this.email = email; } + public Long getId() { + return id; // upewnij się, że pole nazywa się 'id' + } + + public String getEmail() { + return email; // upewnij się, że pole nazywa się 'email' + } } + diff --git a/src/main/java/pl/wsb/fitnesstracker/user/internal/UserController.java b/src/main/java/pl/wsb/fitnesstracker/user/internal/UserController.java index 3f718fd2b..95f0bee3a 100644 --- a/src/main/java/pl/wsb/fitnesstracker/user/internal/UserController.java +++ b/src/main/java/pl/wsb/fitnesstracker/user/internal/UserController.java @@ -13,13 +13,18 @@ */ @RestController @RequestMapping("/v1/users") -@RequiredArgsConstructor class UserController { private final UserServiceImpl userService; private final UserMapper userMapper; + + public UserController(UserServiceImpl userService, UserMapper userMapper) { + this.userService = userService; + this.userMapper = userMapper; + } + @PostMapping public UserDto addUser(@RequestBody UserDto userDto) throws InterruptedException {