From 2d629141b0c6b9b83c05faee25aa537bb5b76ae1 Mon Sep 17 00:00:00 2001 From: Serhii Senkiv Date: Tue, 17 Jun 2025 09:49:37 +0200 Subject: [PATCH] Set up initial project structure with database schema, security configuration, and core entities. - Added core entities: `Car`, `Rental`, `Payment`, `User`, `Role`, `CarType`, and `PaymentStatus`. - Configured database changelog files for `cars`, `users`, `rentals`, and `payments` tables. - Added `MapperConfig` for MapStruct and integrated with Lombok and Maven. - Configured `SecurityConfig` to enable basic security with public access to car-related endpoints. - Added development and production-specific properties files for database configuration. - Enhanced Maven setup with dependencies for Lombok, MapStruct, H2 Database, and Checkstyle plugin. --- pom.xml | 49 +++++++++++-- .../carsharing/config/MapperConfig.java | 12 ++++ .../carsharing/config/SecurityConfig.java | 29 ++++++++ .../java/com/senkiv/carsharing/model/Car.java | 37 ++++++++++ .../com/senkiv/carsharing/model/CarType.java | 8 +++ .../com/senkiv/carsharing/model/Payment.java | 40 +++++++++++ .../carsharing/model/PaymentStatus.java | 6 ++ .../com/senkiv/carsharing/model/Rental.java | 38 ++++++++++ .../com/senkiv/carsharing/model/Role.java | 6 ++ .../com/senkiv/carsharing/model/User.java | 70 +++++++++++++++++++ src/main/resources/application-dev.properties | 7 ++ .../resources/application-prod.properties | 6 ++ src/main/resources/application.properties | 1 - .../changes/01-create-cars-table.yaml | 47 +++++++++++++ .../changes/02-create-users-table.yaml | 47 +++++++++++++ .../changes/03-create-rentals-table.yaml | 54 ++++++++++++++ .../changes/04-create-payments-table.yaml | 37 ++++++++++ .../db/changelog/db.changelog-master.yaml | 9 +++ src/test/resources/application.properties | 5 ++ 19 files changed, 503 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/senkiv/carsharing/config/MapperConfig.java create mode 100644 src/main/java/com/senkiv/carsharing/config/SecurityConfig.java create mode 100644 src/main/java/com/senkiv/carsharing/model/Car.java create mode 100644 src/main/java/com/senkiv/carsharing/model/CarType.java create mode 100644 src/main/java/com/senkiv/carsharing/model/Payment.java create mode 100644 src/main/java/com/senkiv/carsharing/model/PaymentStatus.java create mode 100644 src/main/java/com/senkiv/carsharing/model/Rental.java create mode 100644 src/main/java/com/senkiv/carsharing/model/Role.java create mode 100644 src/main/java/com/senkiv/carsharing/model/User.java create mode 100644 src/main/resources/application-dev.properties create mode 100644 src/main/resources/application-prod.properties delete mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/db/changelog/changes/01-create-cars-table.yaml create mode 100644 src/main/resources/db/changelog/changes/02-create-users-table.yaml create mode 100644 src/main/resources/db/changelog/changes/03-create-rentals-table.yaml create mode 100644 src/main/resources/db/changelog/changes/04-create-payments-table.yaml create mode 100644 src/main/resources/db/changelog/db.changelog-master.yaml create mode 100644 src/test/resources/application.properties diff --git a/pom.xml b/pom.xml index 3b97708..53cb833 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.springframework.boot spring-boot-starter-parent 3.5.0 - + com.senkiv CarSharing @@ -29,6 +29,9 @@ 21 + 1.6.3 + 0.2.0 + checkstyle.xml @@ -55,7 +58,6 @@ org.liquibase liquibase-core - com.mysql mysql-connector-j @@ -76,8 +78,18 @@ spring-security-test test + + org.mapstruct + mapstruct + ${mapstruct.version} + + + com.h2database + h2 + 2.3.232 + test + - @@ -89,6 +101,16 @@ org.projectlombok lombok + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + org.projectlombok + lombok-mapstruct-binding + ${lombok-mapstruct-binding.version} + @@ -104,7 +126,26 @@ + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.3.0 + + + compile + + check + + + + + ${maven.checkstyle.plugin.configLocation} + true + true + false + src + + - diff --git a/src/main/java/com/senkiv/carsharing/config/MapperConfig.java b/src/main/java/com/senkiv/carsharing/config/MapperConfig.java new file mode 100644 index 0000000..6e96fd4 --- /dev/null +++ b/src/main/java/com/senkiv/carsharing/config/MapperConfig.java @@ -0,0 +1,12 @@ +package com.senkiv.carsharing.config; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.NullValueCheckStrategy; + +@org.mapstruct.MapperConfig( + componentModel = "spring", + injectionStrategy = InjectionStrategy.CONSTRUCTOR, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS +) +public class MapperConfig { +} diff --git a/src/main/java/com/senkiv/carsharing/config/SecurityConfig.java b/src/main/java/com/senkiv/carsharing/config/SecurityConfig.java new file mode 100644 index 0000000..b5e1b93 --- /dev/null +++ b/src/main/java/com/senkiv/carsharing/config/SecurityConfig.java @@ -0,0 +1,29 @@ +package com.senkiv.carsharing.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableMethodSecurity +public class SecurityConfig { + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + return http + .csrf(AbstractHttpConfigurer::disable) + .cors(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(auth -> { + auth.requestMatchers("/cars", "/cars/**").permitAll(); + }) + .httpBasic(Customizer.withDefaults()) + .sessionManagement( + session -> session.sessionCreationPolicy( + SessionCreationPolicy.STATELESS)) + .build(); + } +} diff --git a/src/main/java/com/senkiv/carsharing/model/Car.java b/src/main/java/com/senkiv/carsharing/model/Car.java new file mode 100644 index 0000000..61cbcf4 --- /dev/null +++ b/src/main/java/com/senkiv/carsharing/model/Car.java @@ -0,0 +1,37 @@ +package com.senkiv.carsharing.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.math.BigDecimal; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; + +@Entity +@Table(name = "cars") +@Getter +@Setter +@SQLDelete(sql = "UPDATE cars SET is_deleted = true WHERE id = ?;") +@SQLRestriction("is_deleted = false") +public class Car { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(nullable = false) + private String model; + @Column(nullable = false) + private String brand; + @Column(nullable = false) + private CarType type; + @Column(nullable = false) + private int inventory; + @Column(nullable = false) + private BigDecimal dailyFee; + @Column(nullable = false, columnDefinition = "TINYINT") + private boolean isDeleted; +} diff --git a/src/main/java/com/senkiv/carsharing/model/CarType.java b/src/main/java/com/senkiv/carsharing/model/CarType.java new file mode 100644 index 0000000..e0eb684 --- /dev/null +++ b/src/main/java/com/senkiv/carsharing/model/CarType.java @@ -0,0 +1,8 @@ +package com.senkiv.carsharing.model; + +public enum CarType { + SEDAN, + HATCHBACK, + UNIVERSAL, + SUV +} diff --git a/src/main/java/com/senkiv/carsharing/model/Payment.java b/src/main/java/com/senkiv/carsharing/model/Payment.java new file mode 100644 index 0000000..3b9373f --- /dev/null +++ b/src/main/java/com/senkiv/carsharing/model/Payment.java @@ -0,0 +1,40 @@ +package com.senkiv.carsharing.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.math.BigDecimal; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; + +@Entity +@Table(name = "payments") +@Getter +@Setter +@SQLDelete(sql = "UPDATE payments SET is_deleted = true WHERE id = ?") +@SQLRestriction("is_deleted = false") +public class Payment { + @Id + private Long id; + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private PaymentStatus status; + @OneToOne(fetch = FetchType.LAZY, optional = false) + @MapsId + @JoinColumn(name = "rental_id") + private Rental rental; + private String url; + private Long sessionId; + private BigDecimal amountToPay; //calculated rental total price + @Column(nullable = false, columnDefinition = "TINYINT") + private boolean isDeleted; +} diff --git a/src/main/java/com/senkiv/carsharing/model/PaymentStatus.java b/src/main/java/com/senkiv/carsharing/model/PaymentStatus.java new file mode 100644 index 0000000..560982c --- /dev/null +++ b/src/main/java/com/senkiv/carsharing/model/PaymentStatus.java @@ -0,0 +1,6 @@ +package com.senkiv.carsharing.model; + +public enum PaymentStatus { + PENDING, + PAID +} diff --git a/src/main/java/com/senkiv/carsharing/model/Rental.java b/src/main/java/com/senkiv/carsharing/model/Rental.java new file mode 100644 index 0000000..a507fb7 --- /dev/null +++ b/src/main/java/com/senkiv/carsharing/model/Rental.java @@ -0,0 +1,38 @@ +package com.senkiv.carsharing.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import java.time.LocalDate; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; + +@Entity +@Table(name = "rentals") +@SQLDelete(sql = "UPDATE rentals SET is_deleted = true WHERE id = ?") +@SQLRestriction("is_deleted = false") +public class Rental { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(nullable = false) + private LocalDate rentalDate; + @Column(nullable = false) + private LocalDate returnDate; + @Column(nullable = false) + private LocalDate actualReturnDate; + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "car_id") + private Car car; + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "user_id") + private User user; + @Column(nullable = false, columnDefinition = "TINYINT") + private boolean isDeleted; +} diff --git a/src/main/java/com/senkiv/carsharing/model/Role.java b/src/main/java/com/senkiv/carsharing/model/Role.java new file mode 100644 index 0000000..94b1f22 --- /dev/null +++ b/src/main/java/com/senkiv/carsharing/model/Role.java @@ -0,0 +1,6 @@ +package com.senkiv.carsharing.model; + +public enum Role { + MANAGER, + CUSTOMER +} diff --git a/src/main/java/com/senkiv/carsharing/model/User.java b/src/main/java/com/senkiv/carsharing/model/User.java new file mode 100644 index 0000000..c14a726 --- /dev/null +++ b/src/main/java/com/senkiv/carsharing/model/User.java @@ -0,0 +1,70 @@ +package com.senkiv.carsharing.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.util.Collection; +import java.util.List; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +@Entity +@Table(name = "users") +public class User implements UserDetails { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(nullable = false) + private String firstName; + @Column(nullable = false) + private String lastName; + @Column(nullable = false, unique = true) + private String email; + @Column(nullable = false) + private String password; + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private Role role; + @Column(nullable = false, columnDefinition = "TINYINT") + private boolean isDeleted; + + @Override + public Collection getAuthorities() { + return List.of(); + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getUsername() { + return email; + } + + @Override + public boolean isAccountNonExpired() { + return !isDeleted; + } + + @Override + public boolean isAccountNonLocked() { + return !isDeleted; + } + + @Override + public boolean isCredentialsNonExpired() { + return !isDeleted; + } + + @Override + public boolean isEnabled() { + return !isDeleted; + } +} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties new file mode 100644 index 0000000..0d526f9 --- /dev/null +++ b/src/main/resources/application-dev.properties @@ -0,0 +1,7 @@ +spring.application.name=CarSharing +spring.datasource.url=jdbc:mysql://localhost:3306/car_sharing +spring.datasource.username=root +spring.datasource.password=Senkiv1905! +spring.jpa.open-in-view=false +spring.jpa.hibernate.ddl-auto=validate +spring.jpa.show-sql=true diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties new file mode 100644 index 0000000..b7a33e0 --- /dev/null +++ b/src/main/resources/application-prod.properties @@ -0,0 +1,6 @@ +spring.application.name=CarSharing +spring.datasource.url=jdbc:mysql://localhost:3306/car_sharing +spring.datasource.username=root +spring.datasource.password=123 +spring.jpa.open-in-view=false + diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index dfb7f48..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=CarSharing diff --git a/src/main/resources/db/changelog/changes/01-create-cars-table.yaml b/src/main/resources/db/changelog/changes/01-create-cars-table.yaml new file mode 100644 index 0000000..3a88f90 --- /dev/null +++ b/src/main/resources/db/changelog/changes/01-create-cars-table.yaml @@ -0,0 +1,47 @@ +databaseChangeLog: + - changeSet: + id: 1 + author: Serhii Senkiv + changes: + - createTable: + tableName: cars + columns: + - column: + autoIncrement: true + constraints: + nullable: false + primaryKey: true + primaryKeyName: pk_cars + name: id + type: BIGINT + - column: + constraints: + nullable: false + name: model + type: VARCHAR(255) + - column: + constraints: + nullable: false + name: brand + type: VARCHAR(255) + - column: + constraints: + nullable: false + name: type + type: SMALLINT + - column: + constraints: + nullable: false + name: inventory + type: INT + - column: + constraints: + nullable: false + name: daily_fee + type: DECIMAL + - column: + constraints: + nullable: false + name: is_deleted + type: TINYINT + diff --git a/src/main/resources/db/changelog/changes/02-create-users-table.yaml b/src/main/resources/db/changelog/changes/02-create-users-table.yaml new file mode 100644 index 0000000..3a24748 --- /dev/null +++ b/src/main/resources/db/changelog/changes/02-create-users-table.yaml @@ -0,0 +1,47 @@ +databaseChangeLog: + - changeSet: + id: 2 + author: Serhii Senkiv + changes: + - createTable: + columns: + - column: + constraints: + nullable: false + primaryKey: true + primaryKeyName: pk_users + name: id + autoIncrement: true + type: BIGINT + - column: + constraints: + nullable: false + name: first_name + type: VARCHAR(255) + - column: + constraints: + nullable: false + name: last_name + type: VARCHAR(255) + - column: + constraints: + nullable: false + unique: true + name: email + type: VARCHAR(255) + - column: + constraints: + nullable: false + name: password + type: VARCHAR(255) + - column: + constraints: + nullable: false + name: role + type: VARCHAR(255) + - column: + constraints: + nullable: false + name: is_deleted + type: TINYINT + tableName: users diff --git a/src/main/resources/db/changelog/changes/03-create-rentals-table.yaml b/src/main/resources/db/changelog/changes/03-create-rentals-table.yaml new file mode 100644 index 0000000..4e5c655 --- /dev/null +++ b/src/main/resources/db/changelog/changes/03-create-rentals-table.yaml @@ -0,0 +1,54 @@ +databaseChangeLog: + - changeSet: + id: 3 + author: Serhii Senkiv + changes: + - createTable: + columns: + - column: + autoIncrement: true + constraints: + nullable: false + primaryKey: true + primaryKeyName: pk_rentals + name: id + type: BIGINT + - column: + constraints: + nullable: false + name: rental_date + type: DATE + - column: + constraints: + nullable: false + name: return_date + type: DATE + - column: + constraints: + nullable: false + name: actual_return_date + type: DATE + - column: + constraints: + nullable: false + name: is_deleted + type: TINYINT + - column: + constraints: + foreignKeyName: fk_cars + referencedTableName: cars + referencedColumnNames: id + nullable: false + name: car_id + type: BIGINT + - column: + constraints: + foreignKeyName: fk_users + referencedTableName: users + referencedColumnNames: id + nullable: false + name: user_id + type: BIGINT + tableName: rentals + + diff --git a/src/main/resources/db/changelog/changes/04-create-payments-table.yaml b/src/main/resources/db/changelog/changes/04-create-payments-table.yaml new file mode 100644 index 0000000..eeca20d --- /dev/null +++ b/src/main/resources/db/changelog/changes/04-create-payments-table.yaml @@ -0,0 +1,37 @@ +databaseChangeLog: + - changeSet: + id: 4 + author: Serhii Senkiv + changes: + - createTable: + columns: + - column: + constraints: + nullable: false + primaryKey: true + primaryKeyName: pk_payments + foreignKeyName: fk_payments_on_rental + referencedTableName: rentals + referencedColumnNames: id + name: rental_id + type: BIGINT + - column: + constraints: + nullable: false + name: status + type: VARCHAR(255) + - column: + name: url + type: VARCHAR(255) + - column: + name: session_id + type: BIGINT + - column: + name: amount_to_pay + type: DECIMAL + - column: + constraints: + nullable: false + name: is_deleted + type: TINYINT + tableName: payments diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000..e9474ac --- /dev/null +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,9 @@ +databaseChangeLog: + - include: + file: db/changelog/changes/01-create-cars-table.yaml + - include: + file: db/changelog/changes/02-create-users-table.yaml + - include: + file: db/changelog/changes/03-create-rentals-table.yaml + - include: + file: db/changelog/changes/04-create-payments-table.yaml \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..bc2fdde --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,5 @@ +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect