diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..f24c79d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 3704dc6..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=Kotlin.SpringbootV2 diff --git a/.gitattributes b/thisgottawork/.gitattributes similarity index 100% rename from .gitattributes rename to thisgottawork/.gitattributes diff --git a/.gitignore b/thisgottawork/.gitignore similarity index 100% rename from .gitignore rename to thisgottawork/.gitignore diff --git a/.mvn/wrapper/maven-wrapper.properties b/thisgottawork/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from .mvn/wrapper/maven-wrapper.properties rename to thisgottawork/.mvn/wrapper/maven-wrapper.properties diff --git a/mvnw b/thisgottawork/mvnw old mode 100755 new mode 100644 similarity index 100% rename from mvnw rename to thisgottawork/mvnw diff --git a/mvnw.cmd b/thisgottawork/mvnw.cmd similarity index 100% rename from mvnw.cmd rename to thisgottawork/mvnw.cmd diff --git a/pom.xml b/thisgottawork/pom.xml similarity index 64% rename from pom.xml rename to thisgottawork/pom.xml index 163ad53..6fc6ec7 100644 --- a/pom.xml +++ b/thisgottawork/pom.xml @@ -8,11 +8,11 @@ 3.4.4 - com.coded.spring - Ordering + com.hooba + thisgottawork 0.0.1-SNAPSHOT - Kotlin.SpringbootV2 - Kotlin.SpringbootV2 + thisgottawork + Demo project for Spring Boot @@ -31,6 +31,10 @@ 1.9.25 + + org.springframework.boot + spring-boot-starter-data-jpa + org.springframework.boot spring-boot-starter-web @@ -48,6 +52,10 @@ kotlin-stdlib + + org.postgresql + postgresql + org.springframework.boot spring-boot-starter-test @@ -58,8 +66,33 @@ kotlin-test-junit5 test - - + + jakarta.platform + jakarta.jakartaee-api + 10.0.0 + + + jakarta.platform + jakarta.jakartaee-web-api + 10.0.0 + + + org.springframework.security + spring-security-crypto + 6.4.4 + + + org.testng + testng + RELEASE + compile + + + org.springframework.security + spring-security-core + 6.4.4 + + ${project.basedir}/src/main/kotlin ${project.basedir}/src/test/kotlin diff --git a/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/HelloWorldController.kt b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/HelloWorldController.kt new file mode 100644 index 0000000..23d7553 --- /dev/null +++ b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/HelloWorldController.kt @@ -0,0 +1,39 @@ +package com.hooba.thisgottawork + +import com.hooba.thisgottawork.users.UserEntity +import com.hooba.thisgottawork.users.UserRepository +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController + +@RestController +class HelloWorldController( + val usersRepository: UserRepository, +){ + + @GetMapping("/home") + fun helloWorld() = "Welcome to online ordering!" + + + @PostMapping("/add-name") + fun sayMyName( @RequestBody request: SayMyNameRequest) + = usersRepository.save( + UserEntity( + id =request.id, + name = request.name, + age = request.age, + username = request.username, + password = request.password + ) + ) + +} + +data class SayMyNameRequest( + val id: Long?, + val name: String, + val age: Int, + val username: String, + val password: String +) diff --git a/src/main/kotlin/com/coded/spring/ordering/Application.kt b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/ThisgottaworkApplication.kt similarity index 60% rename from src/main/kotlin/com/coded/spring/ordering/Application.kt rename to thisgottawork/src/main/kotlin/com/hooba/thisgottawork/ThisgottaworkApplication.kt index 8554e49..78055b9 100644 --- a/src/main/kotlin/com/coded/spring/ordering/Application.kt +++ b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/ThisgottaworkApplication.kt @@ -1,11 +1,11 @@ -package com.coded.spring.ordering +package com.hooba.thisgottawork import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication @SpringBootApplication -class Application +class ThisgottaworkApplication fun main(args: Array) { - runApplication(*args) + runApplication(*args) } diff --git a/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/authentication/CustomUserDetailsService.kt b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/authentication/CustomUserDetailsService.kt new file mode 100644 index 0000000..559630c --- /dev/null +++ b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/authentication/CustomUserDetailsService.kt @@ -0,0 +1,21 @@ +package com.hooba.thisgottawork.authentication + + +import com.hooba.thisgottawork.users.UserRepository +import org.springframework.security.core.userdetails.* +import org.springframework.stereotype.Service + +@Service +class CustomUserDetailsService( + private val usersRepository: UserRepository +) : UserDetailsService { + override fun loadUserByUsername(username: String): UserDetails { + val user = usersRepository.findByUsername(username) + ?: throw UsernameNotFoundException("User not found") + + return User.builder() + .username(user.username) + .password(user.password) + .build() + } +} \ No newline at end of file diff --git a/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/authentication/SecurityConfig.kt b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/authentication/SecurityConfig.kt new file mode 100644 index 0000000..0a1329d --- /dev/null +++ b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/authentication/SecurityConfig.kt @@ -0,0 +1,34 @@ +//package com.hooba.thisgottawork.authentication +// +//import org.springframework.context.annotation.Bean +//import org.springframework.context.annotation.Configuration +//import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +//import org.springframework.security.crypto.password.PasswordEncoder +// +//import org.springframework.security.config.annotation.web.builders.HttpSecurity +//import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +//import org.springframework.security.core.userdetails.UserDetailsService +//import org.springframework.security.web.SecurityFilterChain +// +//@Configuration +//@EnableWebSecurity +//class SecurityConfig( +// private val userDetailsService: UserDetailsService +//) { +// +// @Bean +// fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder() +// +// @Bean +// fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { +// http.csrf { it.disable() } +// .authorizeHttpRequests { +// it.requestMatchers("/public/**").permitAll() +// .anyRequest().authenticated() +// } +// .formLogin { it.defaultSuccessUrl("/hello", true) } +// .userDetailsService(userDetailsService) +// return http.build(); +// } +//} + diff --git a/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/items/ItemsRepository.kt b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/items/ItemsRepository.kt new file mode 100644 index 0000000..6263c26 --- /dev/null +++ b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/items/ItemsRepository.kt @@ -0,0 +1,28 @@ +package com.hooba.thisgottawork.items + +import com.fasterxml.jackson.annotation.JsonBackReference +import com.hooba.thisgottawork.orders.OrderEntity +import jakarta.inject.Named +import jakarta.persistence.* +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface ItemsRepository: JpaRepository + +@Entity +@Table(name = "items") +data class ItemsEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, + + // maps each item to its parent order using the foreign key items.order_id → orders.id + @ManyToOne + @JoinColumn(name = "order_id") + @JsonBackReference + val order: OrderEntity, + val name: String, + val price: Double +){ + constructor() : this(null, OrderEntity(), "", 0.0) +} \ No newline at end of file diff --git a/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/orders/OrderRepository.kt b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/orders/OrderRepository.kt new file mode 100644 index 0000000..5958041 --- /dev/null +++ b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/orders/OrderRepository.kt @@ -0,0 +1,31 @@ +package com.hooba.thisgottawork.orders + +import com.fasterxml.jackson.annotation.JsonManagedReference +import com.hooba.thisgottawork.items.ItemsEntity +import com.hooba.thisgottawork.users.UserEntity +import jakarta.persistence.* +import org.springframework.data.jpa.repository.JpaRepository +import java.time.LocalDateTime + +interface OrderRepository: JpaRepository + +@Entity +@Table(name = "orders") + data class OrderEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, + + @ManyToOne + @JoinColumn(name = "user_id") + var user: UserEntity, + + var restaurant: String, + + @OneToMany(mappedBy = "order", fetch = FetchType.LAZY) + @JsonManagedReference + val items: List? = null, + var timeOrdered: LocalDateTime? = null +){ + constructor() : this(null, UserEntity(), "",null,null) +} \ No newline at end of file diff --git a/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/orders/OrdersController.kt b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/orders/OrdersController.kt new file mode 100644 index 0000000..a64c322 --- /dev/null +++ b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/orders/OrdersController.kt @@ -0,0 +1,41 @@ +package com.hooba.thisgottawork.orders + +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController + +@RestController +class OrdersController( + private val ordersService: OrdersService +) { + + @GetMapping("/orders") + fun getOrders() = ordersService.getOrders() + + @PostMapping("/orders") + fun addOrder(@RequestBody request: OrderRequest) = + ordersService.addOrder(request) +} + + + +// the DTO (Data Transfer Object) for our orders and items list +data class RequestItem( + val name: String, + val price: Double +) + +data class OrderRequest( + val userId: Long, + val restaurant: String, + val items: List +) + +data class OrderResponseDTO( + val id: Long, + val username: String, + val restaurant: String, + val items: List, + val timeOrdered: String? +) \ No newline at end of file diff --git a/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/orders/OrdersService.kt b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/orders/OrdersService.kt new file mode 100644 index 0000000..e67c818 --- /dev/null +++ b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/orders/OrdersService.kt @@ -0,0 +1,45 @@ +package com.hooba.thisgottawork.orders + +import com.hooba.thisgottawork.items.ItemsEntity +import com.hooba.thisgottawork.users.UserRepository +import org.springframework.stereotype.Service + + +@Service +class OrdersService( + private val orderRepository: OrderRepository, + private var userRepository: UserRepository +) { + fun getOrders(): List = orderRepository.findAll().filter { it.user != null }.sortedBy { it.timeOrdered } + + fun addOrder(request: OrderRequest): OrderResponseDTO { + val user = userRepository.findById(request.userId).orElseThrow { + IllegalArgumentException("User with ID ${request.userId} not found") + } + + val order = orderRepository.save( + OrderEntity( + user = user, + restaurant = request.restaurant + ) + ) + + val items = request.items.map { item -> + ItemsEntity( + order = order, + name = item.name, + price = item.price + ) + } + + return OrderResponseDTO( + id = order.id!!, + username = user.username, + restaurant = order.restaurant, + timeOrdered = order.timeOrdered.toString(), + items = items.map { + RequestItem(name = it.name, price = it.price) + } + ) + } +} \ No newline at end of file diff --git a/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/users/UserRepository.kt b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/users/UserRepository.kt new file mode 100644 index 0000000..014a59a --- /dev/null +++ b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/users/UserRepository.kt @@ -0,0 +1,25 @@ +package com.hooba.thisgottawork.users + +import jakarta.persistence.* +import org.springframework.data.jpa.repository.JpaRepository +import jakarta.inject.Named + +@Named +interface UserRepository : JpaRepository{ + fun findByUsername(userName: String): UserEntity? + fun existsByUsername(username: String): Boolean +} + +@Entity +@Table(name = "users") +data class UserEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, + val name: String, + val age: Int, + val username: String, + val password: String +){ + constructor() : this(null, "",0,"", "") +} \ No newline at end of file diff --git a/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/users/UsersController.kt b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/users/UsersController.kt new file mode 100644 index 0000000..5bc12c1 --- /dev/null +++ b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/users/UsersController.kt @@ -0,0 +1,33 @@ +package com.hooba.thisgottawork.users + +import jakarta.validation.constraints.NotBlank +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController + + +@RestController +class UsersController( + private val usersService: UsersService +) { + + @PostMapping("register") + fun registerUser(@RequestBody request: CreateUserRequest): ResponseEntity { + return usersService.registerUser(request) + + } +} + +data class CreateUserRequest( + @field:NotBlank(message = "Username is required") + val name: String, + + @field:NotBlank(message = "Password is required") + val age: Int, + @field:NotBlank(message = "Username is required") + val username: String, + + @field:NotBlank(message = "Password is required") + val password: String +) \ No newline at end of file diff --git a/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/users/UsersService.kt b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/users/UsersService.kt new file mode 100644 index 0000000..ffab2ce --- /dev/null +++ b/thisgottawork/src/main/kotlin/com/hooba/thisgottawork/users/UsersService.kt @@ -0,0 +1,26 @@ +package com.hooba.thisgottawork.users + +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.stereotype.Service + +@Service +class UsersService( + private val userRepository: UserRepository, +// private val passwordEncoder: PasswordEncoder +) + { + fun registerUser(request: CreateUserRequest): ResponseEntity { + if (userRepository.existsByUsername(request.username)) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(mapOf("error" to "username ${request.username} already exists")) + } + +// val hashedPassword = passwordEncoder.encode(request.password) +// val newUser = UserEntity(name =request.name, age =request.age, username = request.username, password = hashedPassword) +// userRepository.save(newUser) + + return ResponseEntity.ok().build() + } +} \ No newline at end of file diff --git a/thisgottawork/src/main/resources/application.properties b/thisgottawork/src/main/resources/application.properties new file mode 100644 index 0000000..d6c2fb5 --- /dev/null +++ b/thisgottawork/src/main/resources/application.properties @@ -0,0 +1,6 @@ +spring.application.name=thisgottawork +server.port=8081 +spring.datasource.url=jdbc:postgresql://localhost:5432/postgres +spring.datasource.username=postgres +spring.datasource.password=password +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file diff --git a/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt b/thisgottawork/src/test/kotlin/com/hooba/thisgottawork/ThisgottaworkApplicationTests.kt similarity index 67% rename from src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt rename to thisgottawork/src/test/kotlin/com/hooba/thisgottawork/ThisgottaworkApplicationTests.kt index b2e2320..274778b 100644 --- a/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt +++ b/thisgottawork/src/test/kotlin/com/hooba/thisgottawork/ThisgottaworkApplicationTests.kt @@ -1,10 +1,10 @@ -package com.coded.spring.ordering +package com.hooba.thisgottawork import org.junit.jupiter.api.Test import org.springframework.boot.test.context.SpringBootTest @SpringBootTest -class ApplicationTests { +class ThisgottaworkApplicationTests { @Test fun contextLoads() {