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 extends GrantedAuthority> 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