diff --git a/pom.xml b/pom.xml index 163ad53..113864c 100644 --- a/pom.xml +++ b/pom.xml @@ -47,6 +47,47 @@ org.jetbrains.kotlin kotlin-stdlib + + jakarta.inject + jakarta.inject-api + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.postgresql + postgresql + runtime + + + org.springframework.boot + spring-boot-starter-security + + + com.hazelcast + hazelcast + 5.5.0 + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + runtime + 0.11.5 + + + io.jsonwebtoken + jjwt-jackson + runtime + 0.11.5 + + + org.springframework.boot diff --git a/readme.md b/readme.md index e18b898..3a49ac1 100644 --- a/readme.md +++ b/readme.md @@ -1,59 +1,47 @@ - -# Coded Academy: Student Submission Guidelines - -## Online Ordering Server - -## Getting Started - -### 1. Fork the Repository - -1. Click the "Fork" button at the top right of this repository. -2. This will create a copy of the repository in your personal GitHub account. - -### 2. Clone Your Forked Repository - -### 3. Complete Your Assignment - -### 4. Commit Your Changes (Include Task Name in the comment) - -Example: "Online Ordering - Post an Order" - -### 5. Push Your Branch to Your Fork - - -### 6. Submit a Pull Request (PR) - - -## Best Practices - -- Write clean, readable code -- Include comments explaining your logic -- Test your code thoroughly -- Follow the coding standards provided in class - - - -## Submission Checklist - -- [ ] Forked the repository -- [ ] Completed all assignment requirements -- [ ] Committed changes with descriptive messages -- [ ] Pushed branch to your fork -- [ ] Submitted a pull request - -## Need Help? - -If you encounter any issues or have questions: -- Check the course materials -- Consult the documentation -- Ask your instructor or teaching assistants - -## Code of Conduct - -- Be respectful -- Collaborate professionally -- Maintain academic integrity - ---- - -Happy Coding! 🚀👩‍💻👨‍💻 +### project navi/info + +`src/main/kotlin/com.coded.spring.ordering` is where all the packages are + +`dtos` ←→ `controller` → `service` → `repository` → `entity` + +`controller` = entry point for http requests (receive requests, process data, determine response) +- `GET`: retrieve data from a server +- `POST`: send data to the server to create a new resource +- `PUT`: update or replace an existing resource +- `DELETE`: remove a resource from the server + +`service` = business logic (processes data, makes decisions, eg: save order) + +`repository` = database operations (handles communication with db: save, find, delete) + +`entity` = domain model (defines what your data is in the system: fields, relationships) + +`dto` = data transfer object (cleans request/response json bodies, to avoid returning entities) + +### services +- welcome page +- list all orders +- create new order + +### progress +#### ultimate goal is to catch up!!!! +- [x] exercise 1: welcome + - [x] bonus: checked with `curl` command +- [x] exercise 2: endpoint to `POST` order + - [x] bonus: add `createdAt` column and sort +- [X] exercise 3: create + connect db + - [x] bonus: create `items` table and connect it to `orders` +- [x] exercise 4: user authentication + - [x] bonus: password validation so people can't create a weak password +- [ ] exercise 5: user profiles + - [ ] bonus: +- [ ] exercise 6: unit testing + - [ ] bonus: +- [ ] exercise 7: menu endpoint + - [ ] bonus: +- [ ] exercise 8: configuration + - [ ] bonus: +- [ ] exercise 9: setup swagger + - [ ] bonus: +- [ ] exercise 10: refactor to micro services + - [ ] bonus: \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/Application.kt b/src/main/kotlin/com/coded/spring/ordering/Application.kt index 8554e49..fb02c06 100644 --- a/src/main/kotlin/com/coded/spring/ordering/Application.kt +++ b/src/main/kotlin/com/coded/spring/ordering/Application.kt @@ -8,4 +8,4 @@ class Application fun main(args: Array) { runApplication(*args) -} +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/CustomExceptions.kt b/src/main/kotlin/com/coded/spring/ordering/CustomExceptions.kt new file mode 100644 index 0000000..85c87ca --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/CustomExceptions.kt @@ -0,0 +1,7 @@ +package com.coded.spring.ordering + +open class OrderException(message: String) : RuntimeException(message) + +class UserIdNotFound(userId: Long): OrderException("User ID $userId not found.") +class InvalidPasswordException(reason: String): OrderException("Invalid password: $reason") +class UsernameAlreadyExistsException(): OrderException("Username already exists.") \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/GlobalExceptionHandler.kt b/src/main/kotlin/com/coded/spring/ordering/GlobalExceptionHandler.kt new file mode 100644 index 0000000..421729a --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/GlobalExceptionHandler.kt @@ -0,0 +1,16 @@ +package com.coded.spring.ordering + +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* + +@RestControllerAdvice +class GlobalExceptionHandler { + + @ExceptionHandler(OrderException::class) + fun handleOrderingException(ex: OrderException): ResponseEntity> { + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .body(mapOf("error" to ex.message!!)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/AuthenticationController.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/AuthenticationController.kt new file mode 100644 index 0000000..28765f0 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/authentication/AuthenticationController.kt @@ -0,0 +1,38 @@ +package com.coded.spring.ordering.authentication + +import com.coded.spring.ordering.dto.AuthenticationRequest +import com.coded.spring.ordering.dto.AuthenticationResponse +import com.coded.spring.ordering.dto.FailureResponse +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.security.authentication.* +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.core.userdetails.UsernameNotFoundException +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping() +class AuthenticationController( + private val authenticationManager: AuthenticationManager, + private val userDetailsService: UserDetailsService, + private val authenticationService: AuthenticationService +) { + + @PostMapping("/login") + fun login(@RequestBody authRequest: AuthenticationRequest): ResponseEntity<*> { + return try { + val authToken = UsernamePasswordAuthenticationToken(authRequest.username, authRequest.password) + val authentication = authenticationManager.authenticate(authToken) + + if (authentication.isAuthenticated) { + val userDetails = userDetailsService.loadUserByUsername(authRequest.username) + val token = authenticationService.generateToken(userDetails.username) + ResponseEntity.ok(AuthenticationResponse(token)) + } else { + throw UsernameNotFoundException("Invalid user request!") + } + } catch (e: BadCredentialsException) { + ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(FailureResponse("Invalid username or password.")) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/AuthenticationFilter.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/AuthenticationFilter.kt new file mode 100644 index 0000000..1a3a561 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/authentication/AuthenticationFilter.kt @@ -0,0 +1,47 @@ +package com.coded.spring.ordering.authentication + +import jakarta.servlet.FilterChain +import jakarta.servlet.http.* +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource +import org.springframework.stereotype.Component +import org.springframework.web.filter.OncePerRequestFilter +import kotlin.text.startsWith +import kotlin.text.substring + +@Component +class AuthenticationFilter( + private val authenticationService: AuthenticationService, + private val userDetailsService: UserDetailsService +) : OncePerRequestFilter() { + + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + val authHeader = request.getHeader("Authorization") + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response) + return + } + + val token = authHeader.substring(7) + val username = authenticationService.extractUsername(token) + + if (SecurityContextHolder.getContext().authentication == null) { + val userDetails = userDetailsService.loadUserByUsername(username) + if (authenticationService.isTokenValid(token, userDetails.username)) { + val authToken = UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.authorities + ) + authToken.details = WebAuthenticationDetailsSource().buildDetails(request) + SecurityContextHolder.getContext().authentication = authToken + } + } + + filterChain.doFilter(request, response) + } +} diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/AuthenticationService.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/AuthenticationService.kt new file mode 100644 index 0000000..0336445 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/authentication/AuthenticationService.kt @@ -0,0 +1,42 @@ +package com.coded.spring.ordering.authentication + +import io.jsonwebtoken.* +import io.jsonwebtoken.security.Keys +import org.springframework.stereotype.Component +import java.util.* +import javax.crypto.SecretKey + +@Component +class AuthenticationService { + + private val secretKey: SecretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256) + private val expirationMs: Long = 1000 * 60 * 60 // milliseconds * seconds * minutes = 1 hour + + fun generateToken(username: String): String { + val now = Date() + val expiry = Date(now.time + expirationMs) + + return Jwts.builder() + .setSubject(username) + .setIssuedAt(now) + .setExpiration(expiry) + .signWith(secretKey) + .compact() + } + + fun extractUsername(token: String): String = + Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token) + .body + .subject + + fun isTokenValid(token: String, username: String): Boolean { + return try { + extractUsername(token) == username + } catch (e: Exception) { + false + } + } +} diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/CustomUserDetailsService.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/CustomUserDetailsService.kt new file mode 100644 index 0000000..6ca53dc --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/authentication/CustomUserDetailsService.kt @@ -0,0 +1,22 @@ +package com.coded.spring.ordering.authentication + +import com.coded.spring.ordering.repository.UserRepository +import org.springframework.security.core.userdetails.* +import org.springframework.stereotype.Service +import org.springframework.security.core.userdetails.User + +@Service +class CustomUserDetailsService( + private val userRepository: UserRepository): UserDetailsService { + + override fun loadUserByUsername(username: String): UserDetails { + val user = userRepository.findByUsername(username) + ?: throw UsernameNotFoundException("User not found") + + return User.builder() + .username(user.username) + .password(user.password) + .authorities(emptyList()) + .build() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/SecurityConfig.kt new file mode 100644 index 0000000..d108b0c --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/authentication/SecurityConfig.kt @@ -0,0 +1,59 @@ +package com.coded.spring.ordering.authentication + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.authentication.* +import org.springframework.security.authentication.dao.DaoAuthenticationProvider +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +import org.springframework.security.config.http.SessionCreationPolicy +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.security.web.SecurityFilterChain +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter + +@Configuration +@EnableWebSecurity +class SecurityConfig( + private val jwtAuthFilter: AuthenticationFilter, + private val userDetailsService: UserDetailsService +) { + + @Bean + fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { + http.csrf { it.disable() } + .authorizeHttpRequests { + it.requestMatchers("/login", + "/register", + "/welcome", + "/error" + ).permitAll() + + .anyRequest().authenticated() + } + .sessionManagement { + it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + } + .authenticationProvider(authenticationProvider()) + .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter::class.java) + + return http.build() + } + + @Bean + fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder() + + @Bean + fun authenticationManager(config: AuthenticationConfiguration): AuthenticationManager = + config.authenticationManager + + @Bean + fun authenticationProvider(): AuthenticationProvider { + val provider = DaoAuthenticationProvider() + provider.setUserDetailsService(userDetailsService) + provider.setPasswordEncoder(passwordEncoder()) + return provider + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/controller/OrderController.kt b/src/main/kotlin/com/coded/spring/ordering/controller/OrderController.kt new file mode 100644 index 0000000..8262205 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/controller/OrderController.kt @@ -0,0 +1,32 @@ +package com.coded.spring.ordering.controller + +import com.coded.spring.ordering.OrderException +import com.coded.spring.ordering.dto.AuthenticationRequest +import com.coded.spring.ordering.dto.FailureResponse +import com.coded.spring.ordering.dto.OrderRequest +import com.coded.spring.ordering.entity.OrderEntity +import com.coded.spring.ordering.service.OrderService +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* + +@RestController +class OrderController(val orderService: OrderService) { + + @GetMapping("/welcome") + fun welcome() = "Welcome to our Online Ordering Server!" + + @PostMapping("/register") + fun registerUser(@RequestBody request: AuthenticationRequest) { + orderService.registerUser(request) + } + + @GetMapping("/order") + fun getAll(): List { + return orderService.getAllOrders() + } + + @PostMapping("/order") + fun saveOrder(@RequestBody request: OrderRequest): String { + return orderService.createOrder(request) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/dto/AuthenticationRequest.kt b/src/main/kotlin/com/coded/spring/ordering/dto/AuthenticationRequest.kt new file mode 100644 index 0000000..6f9c208 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/dto/AuthenticationRequest.kt @@ -0,0 +1,6 @@ +package com.coded.spring.ordering.dto + +data class AuthenticationRequest ( + val username: String, + val password: String +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/dto/AuthenticationResponse.kt b/src/main/kotlin/com/coded/spring/ordering/dto/AuthenticationResponse.kt new file mode 100644 index 0000000..11e536a --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/dto/AuthenticationResponse.kt @@ -0,0 +1,5 @@ +package com.coded.spring.ordering.dto + +class AuthenticationResponse ( + val token: String +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/dto/FailureResponse.kt b/src/main/kotlin/com/coded/spring/ordering/dto/FailureResponse.kt new file mode 100644 index 0000000..c9eb6df --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/dto/FailureResponse.kt @@ -0,0 +1,5 @@ +package com.coded.spring.ordering.dto + +data class FailureResponse( + val error: String +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/dto/OrderRequest.kt b/src/main/kotlin/com/coded/spring/ordering/dto/OrderRequest.kt new file mode 100644 index 0000000..7754bbc --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/dto/OrderRequest.kt @@ -0,0 +1,7 @@ +package com.coded.spring.ordering.dto + +data class OrderRequest( + val userId: Long, + val restaurant: String, + val items: List +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/entity/ItemEntity.kt b/src/main/kotlin/com/coded/spring/ordering/entity/ItemEntity.kt new file mode 100644 index 0000000..ba200c1 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/entity/ItemEntity.kt @@ -0,0 +1,20 @@ +package com.coded.spring.ordering.entity + +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Table + +@Entity +@Table(name = "items") +data class ItemEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long, + + val name: String +) { + + constructor(): this(0, "") +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/entity/OrderEntity.kt b/src/main/kotlin/com/coded/spring/ordering/entity/OrderEntity.kt new file mode 100644 index 0000000..24d2b78 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/entity/OrderEntity.kt @@ -0,0 +1,34 @@ +package com.coded.spring.ordering.entity + +import jakarta.persistence.Entity +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.LocalDateTime + +@Entity +@Table(name = "orders") +data class OrderEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long, + + val userId: Long, + val createdAt: LocalDateTime, + val restaurant: String, + + @ManyToOne + @JoinColumn(name = "items_id") + val items: ItemEntity, +) { + constructor(): this( + 0, + 0, + LocalDateTime.now(), + "", + ItemEntity() + ) +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/entity/UserEntity.kt b/src/main/kotlin/com/coded/spring/ordering/entity/UserEntity.kt new file mode 100644 index 0000000..c6fd1b6 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/entity/UserEntity.kt @@ -0,0 +1,21 @@ +package com.coded.spring.ordering.entity + +import jakarta.persistence.* + +@Entity +@Table(name = "users") +class UserEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long, + + var username: String, + var password: String, + + ) { + constructor(): this( + 0, + "", + "", + ) +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/repository/ItemRepository.kt b/src/main/kotlin/com/coded/spring/ordering/repository/ItemRepository.kt new file mode 100644 index 0000000..662f8a3 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/repository/ItemRepository.kt @@ -0,0 +1,10 @@ +package com.coded.spring.ordering.repository + +import com.coded.spring.ordering.entity.ItemEntity +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface ItemRepository : JpaRepository { + fun findByName(name: String): ItemEntity? +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/repository/OrderRepository.kt b/src/main/kotlin/com/coded/spring/ordering/repository/OrderRepository.kt new file mode 100644 index 0000000..5ec78a5 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/repository/OrderRepository.kt @@ -0,0 +1,10 @@ +package com.coded.spring.ordering.repository + +import com.coded.spring.ordering.entity.OrderEntity +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface OrderRepository: JpaRepository { + fun findAllByOrderByCreatedAtAsc(): List +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/repository/UserRepository.kt b/src/main/kotlin/com/coded/spring/ordering/repository/UserRepository.kt new file mode 100644 index 0000000..ffb2ac9 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/repository/UserRepository.kt @@ -0,0 +1,10 @@ +package com.coded.spring.ordering.repository + +import com.coded.spring.ordering.entity.UserEntity +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface UserRepository : JpaRepository { + fun findByUsername(username: String): UserEntity? +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/service/OrderService.kt b/src/main/kotlin/com/coded/spring/ordering/service/OrderService.kt new file mode 100644 index 0000000..30ce0e6 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/service/OrderService.kt @@ -0,0 +1,69 @@ +package com.coded.spring.ordering.service + +import com.coded.spring.ordering.UserIdNotFound +import com.coded.spring.ordering.InvalidPasswordException +import com.coded.spring.ordering.UsernameAlreadyExistsException +import com.coded.spring.ordering.dto.AuthenticationRequest +import com.coded.spring.ordering.dto.OrderRequest +import com.coded.spring.ordering.entity.ItemEntity +import com.coded.spring.ordering.entity.OrderEntity +import com.coded.spring.ordering.entity.UserEntity +import com.coded.spring.ordering.repository.ItemRepository +import com.coded.spring.ordering.repository.OrderRepository +import com.coded.spring.ordering.repository.UserRepository +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.stereotype.Service +import java.time.LocalDateTime + +@Service +class OrderService( + private val orderRepository: OrderRepository, + private val itemRepository: ItemRepository, + private val userRepository: UserRepository, + private val passwordEncoder: PasswordEncoder +) { + + fun registerUser(request: AuthenticationRequest) { + if (userRepository.findByUsername(request.username) != null) + throw UsernameAlreadyExistsException() + if (request.password.length < 6) + throw InvalidPasswordException("Password must be at least 6 characters long.") + if (!request.password.any { it.isUpperCase() }) + throw InvalidPasswordException("Password must contain at least one uppercase letter.") + if (!request.password.any { it.isDigit() }) + throw InvalidPasswordException("Password must contain at least one number.") + + val user = UserEntity( + 0, + request.username, + passwordEncoder.encode(request.password) + ) + + userRepository.save(user) + } + + fun getAllOrders(): List { + return orderRepository.findAllByOrderByCreatedAtAsc() + } + + fun createOrder(request: OrderRequest): String { + val user = userRepository.findById(request.userId) + .orElseThrow { UserIdNotFound(request.userId) } + + val itemsToString = request.items.joinToString(", ") + val existingItem = itemRepository.findByName(itemsToString) + val item = existingItem ?: itemRepository.save(ItemEntity(0, itemsToString)) + + val order = OrderEntity( + 0, + user.id, + LocalDateTime.now(), + request.restaurant, + item + ) + + orderRepository.save(order) + return "Order created." + } + +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3704dc6..d4154e4 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,7 @@ spring.application.name=Kotlin.SpringbootV2 + +spring.datasource.url=jdbc:postgresql://localhost:5432/OrderDatabase +spring.datasource.username=postgres +spring.datasource.password=thepassword +spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect +spring.jpa.show-sql=true