From 17c290f3f5b28bf091e1dc59d54de1fd33c4680f Mon Sep 17 00:00:00 2001 From: Zainab Date: Thu, 10 Apr 2025 19:31:18 +0300 Subject: [PATCH 01/11] Added a welcome message --- pom.xml | 32 +++++++++++++++++++ .../spring/ordering/TalabatController.kt | 20 ++++++++++++ src/main/resources/application.properties | 5 +++ 3 files changed, 57 insertions(+) create mode 100644 src/main/kotlin/com/coded/spring/ordering/TalabatController.kt diff --git a/pom.xml b/pom.xml index 163ad53..e79f0f2 100644 --- a/pom.xml +++ b/pom.xml @@ -43,11 +43,43 @@ org.jetbrains.kotlin kotlin-reflect + + org.postgresql + postgresql + + + jakarta.inject + jakarta.inject-api + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + org.jetbrains.kotlin kotlin-stdlib + + com.fasterxml.jackson.core + jackson-databind + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + org.springframework.boot spring-boot-starter-test diff --git a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt new file mode 100644 index 0000000..4d7fee0 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt @@ -0,0 +1,20 @@ +package com.coded.spring.ordering + +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 +import org.springframework.beans.factory.annotation.Value +import jakarta.persistence.Column + +@RestController +class TalabatController( +// val talabatRepository: TalabatRepository, +// @Value("\${server-welcome-message}") +// val welcomeMessage: String +){ + + @GetMapping("/welcome") + fun welcomeToTalabat() = "Hello! welcome to Talabat"} + + diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3704dc6..ce2a737 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,6 @@ spring.application.name=Kotlin.SpringbootV2 + +spring.datasource.url=jdbc:postgresql://localhost:5432/myHelloDatabase +spring.datasource.username=postgres +spring.datasource.password=NBKPostgres +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect From de79f233f0c23c2b66e9acfd3e6a38931a71dc9b Mon Sep 17 00:00:00 2001 From: Zainab Date: Thu, 10 Apr 2025 20:01:53 +0300 Subject: [PATCH 02/11] User DataBase connection established, inabling adding new users to users table --- .../spring/ordering/TalabatController.kt | 26 ++++++++++++++----- .../spring/ordering/TalabatRepository.kt | 19 ++++++++++++++ src/main/resources/application.properties | 3 ++- 3 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt index 4d7fee0..e1de56d 100644 --- a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt @@ -5,16 +5,30 @@ import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RestController import org.springframework.beans.factory.annotation.Value -import jakarta.persistence.Column + @RestController class TalabatController( -// val talabatRepository: TalabatRepository, -// @Value("\${server-welcome-message}") -// val welcomeMessage: String -){ + val talabatRepository: TalabatRepository, + @Value("\${server-welcome-message}") + val welcomeMessage: String +) { @GetMapping("/welcome") - fun welcomeToTalabat() = "Hello! welcome to Talabat"} + fun welcomeToTalabat() = "Hello! $welcomeMessage" + + @PostMapping("/clients") + //in memory method to save the information in a list +//fun order( @RequestBody request: OrderRequest) = "Hi ${request.name}, thank you for placing an order from ${request.restaurant} containing the following items: ${request.items} !" + fun clients(@RequestBody request: ClientsRequest) = talabatRepository.save(UserEntity(name = request.name)) + +} + +data class ClientsRequest( + val name: String) +// val restaurant: String, +// val items:List) + + diff --git a/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt b/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt new file mode 100644 index 0000000..50f340c --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt @@ -0,0 +1,19 @@ +package com.coded.spring.ordering + +import jakarta.inject.Named +import jakarta.persistence.* +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface TalabatRepository : JpaRepository + +@Entity +@Table(name = "users") +data class UserEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, + var name: String, +){ + constructor() : this(null, "") +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index ce2a737..e16c324 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,7 @@ spring.application.name=Kotlin.SpringbootV2 -spring.datasource.url=jdbc:postgresql://localhost:5432/myHelloDatabase +spring.datasource.url=jdbc:postgresql://localhost:5432/talabatDB spring.datasource.username=postgres spring.datasource.password=NBKPostgres spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +server-welcome-message=Welcome to Talabat! Where all your favourite restaurants are a click away \ No newline at end of file From b3c058630a188e68dfb62e6a8ddb077f92b4a705 Mon Sep 17 00:00:00 2001 From: Zainab Date: Sat, 12 Apr 2025 23:08:16 +0300 Subject: [PATCH 03/11] A connection between Users and Orders was made! --- .../spring/ordering/TalabatController.kt | 2 -- .../ordering/orders/CreateOrderRequest.kt | 12 ++++++++ .../spring/ordering/orders/ItemsEntity.kt | 20 +++++++++++++ .../spring/ordering/orders/ItemsRepository.kt | 7 +++++ .../spring/ordering/orders/OrderEntity.kt | 28 ++++++++++++++++++ .../ordering/orders/OrdersController.kt | 20 +++++++++++++ .../ordering/orders/OrdersRepository.kt | 6 ++++ .../spring/ordering/orders/OrdersService.kt | 29 +++++++++++++++++++ 8 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/ordering/orders/CreateOrderRequest.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/orders/ItemsEntity.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/orders/ItemsRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/orders/OrderEntity.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/orders/OrdersController.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/orders/OrdersRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/orders/OrdersService.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt index e1de56d..e12e5d9 100644 --- a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt @@ -18,8 +18,6 @@ class TalabatController( fun welcomeToTalabat() = "Hello! $welcomeMessage" @PostMapping("/clients") - //in memory method to save the information in a list -//fun order( @RequestBody request: OrderRequest) = "Hi ${request.name}, thank you for placing an order from ${request.restaurant} containing the following items: ${request.items} !" fun clients(@RequestBody request: ClientsRequest) = talabatRepository.save(UserEntity(name = request.name)) } diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/CreateOrderRequest.kt b/src/main/kotlin/com/coded/spring/ordering/orders/CreateOrderRequest.kt new file mode 100644 index 0000000..4797c75 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/orders/CreateOrderRequest.kt @@ -0,0 +1,12 @@ +package com.coded.spring.ordering.orders + +data class CreateOrderRequest( + val userId: Long, + val restaurant: String, + val items: List +) + +data class ItemRequest( + val name: String, + val quantity: Int +) diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/ItemsEntity.kt b/src/main/kotlin/com/coded/spring/ordering/orders/ItemsEntity.kt new file mode 100644 index 0000000..f6cb177 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/orders/ItemsEntity.kt @@ -0,0 +1,20 @@ +package com.coded.spring.ordering.orders + +import com.coded.spring.ordering.orders.entities.OrderEntity +import jakarta.persistence.* + +@Entity +@Table(name = "items") +data class ItemEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, + + val name: String, + val quantity: Int, + + @ManyToOne + @JoinColumn(name = "order_id") + val order: OrderEntity +) + diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/ItemsRepository.kt b/src/main/kotlin/com/coded/spring/ordering/orders/ItemsRepository.kt new file mode 100644 index 0000000..45f58fa --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/orders/ItemsRepository.kt @@ -0,0 +1,7 @@ +package com.coded.spring.ordering.orders + +import org.springframework.data.jpa.repository.JpaRepository +import com.coded.spring.ordering.orders.ItemEntity + +interface ItemsRepository : JpaRepository + diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/OrderEntity.kt b/src/main/kotlin/com/coded/spring/ordering/orders/OrderEntity.kt new file mode 100644 index 0000000..47a12ac --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/orders/OrderEntity.kt @@ -0,0 +1,28 @@ +package com.coded.spring.ordering.orders.entities + +import com.coded.spring.ordering.UserEntity +import com.coded.spring.ordering.orders.ItemEntity +import jakarta.persistence.* + +@Entity +@Table(name = "orders") +data class OrderEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, + + @ManyToOne + @JoinColumn(name = "name_id") + val user: UserEntity, + + val restaurant: String, + + @Column(columnDefinition = "TEXT") + val items: String = "" +) + + { + constructor() : this(null, UserEntity(), "", "") +} + + diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/OrdersController.kt b/src/main/kotlin/com/coded/spring/ordering/orders/OrdersController.kt new file mode 100644 index 0000000..0cae779 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/orders/OrdersController.kt @@ -0,0 +1,20 @@ +package com.coded.spring.ordering.orders + +import com.coded.spring.ordering.orders.entities.OrderEntity +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping("/orders") +class OrdersController( + private val ordersService: OrdersService +) { + @PostMapping + fun createOrder(@RequestBody request: CreateOrderRequest): OrderEntity { + return ordersService.createOrder(request) + } + @GetMapping("/v1/list") + fun listOrders(): List { + return ordersService.listOrders() + } +} + diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/OrdersRepository.kt b/src/main/kotlin/com/coded/spring/ordering/orders/OrdersRepository.kt new file mode 100644 index 0000000..0b9e993 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/orders/OrdersRepository.kt @@ -0,0 +1,6 @@ +package com.coded.spring.ordering.orders + +import org.springframework.data.jpa.repository.JpaRepository +import com.coded.spring.ordering.orders.entities.OrderEntity + +interface OrderRepository : JpaRepository diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/OrdersService.kt b/src/main/kotlin/com/coded/spring/ordering/orders/OrdersService.kt new file mode 100644 index 0000000..5d77131 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/orders/OrdersService.kt @@ -0,0 +1,29 @@ +package com.coded.spring.ordering.orders + +import com.coded.spring.ordering.TalabatRepository +import com.coded.spring.ordering.orders.entities.OrderEntity +import com.fasterxml.jackson.databind.ObjectMapper +import org.springframework.stereotype.Service + +@Service +class OrdersService( + private val orderRepository: OrderRepository, + private val userRepository: TalabatRepository, + private val objectMapper: ObjectMapper +) { + fun createOrder(request: CreateOrderRequest): OrderEntity { + val user = userRepository.findById(request.userId).orElseThrow() + val itemsJson = objectMapper.writeValueAsString(request.items) + + val order = OrderEntity( + user = user, + restaurant = request.restaurant, + items = itemsJson + ) + + return orderRepository.save(order) + } + fun listOrders(): List { + return orderRepository.findAll() + } +} From a7145ca4a1e4874a20ce5121a23e7d6f436c48fd Mon Sep 17 00:00:00 2001 From: Zainab Date: Thu, 17 Apr 2025 00:31:49 +0300 Subject: [PATCH 04/11] User Authentication task completed --- pom.xml | 5 +++ .../coded/spring/ordering/SecurityConfig.kt | 32 +++++++++++++++++++ .../spring/ordering/TalabatController.kt | 30 ++++++++++------- .../spring/ordering/TalabatRepository.kt | 12 +++++-- .../spring/ordering/UserDetailsService.kt | 21 ++++++++++++ .../spring/ordering/menu/menuController.kt | 22 +++++++++++++ .../coded/spring/ordering/menu/menuEntity.kt | 16 ++++++++++ .../spring/ordering/menu/menuRepository.kt | 5 +++ .../coded/spring/ordering/menu/menuService.kt | 20 ++++++++++++ 9 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/UserDetailsService.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/menu/menuController.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/menu/menuEntity.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/menu/menuRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt diff --git a/pom.xml b/pom.xml index e79f0f2..43953f3 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,11 @@ jakarta.inject jakarta.inject-api + + + org.springframework.boot + spring-boot-starter-security + org.springframework.boot spring-boot-starter-data-jpa diff --git a/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt new file mode 100644 index 0000000..eb071fa --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt @@ -0,0 +1,32 @@ +package com.coded.spring.ordering + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +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.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder +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("/menu", "/clients", "/welcome").permitAll() + it.anyRequest().authenticated() + } + .formLogin { it.defaultSuccessUrl("/welcome", true) } + .httpBasic {} + return http.build() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt index e12e5d9..f7ae218 100644 --- a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt @@ -1,29 +1,37 @@ package com.coded.spring.ordering -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 +import org.springframework.web.bind.annotation.* import org.springframework.beans.factory.annotation.Value - +import org.springframework.security.crypto.password.PasswordEncoder @RestController class TalabatController( - val talabatRepository: TalabatRepository, - @Value("\${server-welcome-message}") - val welcomeMessage: String + private val talabatRepository: TalabatRepository, + private val passwordEncoder: PasswordEncoder, + @Value("\${server-welcome-message}") + val welcomeMessage: String ) { @GetMapping("/welcome") fun welcomeToTalabat() = "Hello! $welcomeMessage" @PostMapping("/clients") - fun clients(@RequestBody request: ClientsRequest) = talabatRepository.save(UserEntity(name = request.name)) - + fun clients(@RequestBody request: ClientsRequest): UserEntity { + val encodedPassword = passwordEncoder.encode(request.password) + return talabatRepository.save( + UserEntity( + name = request.name, + username = request.username, + password = encodedPassword + ) + ) + } } data class ClientsRequest( - val name: String) + val name: String, + val username: String, + val password: String) // val restaurant: String, // val items:List) diff --git a/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt b/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt index 50f340c..642bab1 100644 --- a/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt +++ b/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt @@ -5,7 +5,9 @@ import jakarta.persistence.* import org.springframework.data.jpa.repository.JpaRepository @Named -interface TalabatRepository : JpaRepository +interface TalabatRepository : JpaRepository{ + fun findByUsername(username: String): UserEntity? +} @Entity @Table(name = "users") @@ -14,6 +16,10 @@ data class UserEntity( @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null, var name: String, -){ - constructor() : this(null, "") + @Column(unique = true) + var username: String, + + var password: String +) { + constructor() : this(null, "", "","") } \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/UserDetailsService.kt b/src/main/kotlin/com/coded/spring/ordering/UserDetailsService.kt new file mode 100644 index 0000000..2f5c79b --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/UserDetailsService.kt @@ -0,0 +1,21 @@ +package com.coded.spring.ordering + + +import org.springframework.security.core.userdetails.* +import org.springframework.stereotype.Service + +@Service +class CustomUserDetailsService( + private val talabatRepository: TalabatRepository +) : UserDetailsService { + override fun loadUserByUsername(username: String): UserDetails { + val user = talabatRepository.findByUsername(username) + ?: throw UsernameNotFoundException("User not found") + + return User.builder() + .username(user.username) + .password(user.password) + .roles("USER") + .build() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuController.kt b/src/main/kotlin/com/coded/spring/ordering/menu/menuController.kt new file mode 100644 index 0000000..38abdb4 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/menu/menuController.kt @@ -0,0 +1,22 @@ +package com.coded.spring.ordering.menu + +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping("/menu") +class MenuController(private val menuService: MenuService) { + + @GetMapping + fun getMenu(): List = menuService.getMenu() + + @PostMapping + fun addMenuItem(@RequestBody request: MenuRequest): MenuEntity { + return menuService.addMenuItem(request) + } +} + +data class MenuRequest( + val name: String, + val price: Double +) + diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuEntity.kt b/src/main/kotlin/com/coded/spring/ordering/menu/menuEntity.kt new file mode 100644 index 0000000..6adeb95 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/menu/menuEntity.kt @@ -0,0 +1,16 @@ +package com.coded.spring.ordering.menu + + +import jakarta.persistence.* + +@Entity +@Table(name = "menu") +data class MenuEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, + + val name: String = "", + + val price: Double = 0.0 +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuRepository.kt b/src/main/kotlin/com/coded/spring/ordering/menu/menuRepository.kt new file mode 100644 index 0000000..f8ef99a --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/menu/menuRepository.kt @@ -0,0 +1,5 @@ +package com.coded.spring.ordering.menu + +import org.springframework.data.jpa.repository.JpaRepository + +interface MenuRepository : JpaRepository \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt b/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt new file mode 100644 index 0000000..288c851 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt @@ -0,0 +1,20 @@ +package com.coded.spring.ordering.menu + +import org.springframework.cache.annotation.Cacheable +import org.springframework.stereotype.Service + +@Service +class MenuService(private val menuRepository: MenuRepository) { + + @Cacheable("menu") + fun getMenu(): List { + println("Fetching menu from DB...") + return menuRepository.findAll() + } + + fun addMenuItem(request: MenuRequest): MenuEntity { + val newItem = MenuEntity(name = request.name, price = request.price) + return menuRepository.save(newItem) + } + +} \ No newline at end of file From daab3738d833a3be52a9de8fa333d9a9ba88a079 Mon Sep 17 00:00:00 2001 From: Zainab Date: Thu, 17 Apr 2025 13:19:03 +0300 Subject: [PATCH 05/11] Authenticated users can create a profile --- .../ordering/profiles/profileController.kt | 36 +++++++++++++++++++ .../spring/ordering/profiles/profileEntity.kt | 19 ++++++++++ .../ordering/profiles/profileRepository.kt | 7 ++++ 3 files changed, 62 insertions(+) create mode 100644 src/main/kotlin/com/coded/spring/ordering/profiles/profileController.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/profiles/profileEntity.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/profiles/profileRepository.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/profiles/profileController.kt b/src/main/kotlin/com/coded/spring/ordering/profiles/profileController.kt new file mode 100644 index 0000000..65063aa --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/profiles/profileController.kt @@ -0,0 +1,36 @@ +package com.coded.spring.ordering.profiles + +import com.coded.spring.ordering.TalabatRepository +import org.springframework.web.bind.annotation.* +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.security.core.userdetails.UserDetails + +@RestController +class ProfileController( + private val talabatRepository: TalabatRepository, + private val profileRepository: ProfileRepository +) { + + @PostMapping("/profile") + fun createProfile( + @RequestBody profileRequest: ProfileRequest, + @AuthenticationPrincipal userDetails: UserDetails + ): ProfileEntity { + val user = talabatRepository.findByUsername(userDetails.username) + ?: throw RuntimeException("User not found") + + val profile = ProfileEntity( + user = user, + firstName = profileRequest.firstName, + lastName = profileRequest.lastName, + phoneNumber = profileRequest.phoneNumber + ) + + return profileRepository.save(profile) + } +} +data class ProfileRequest( + val firstName: String, + val lastName: String, + val phoneNumber: String +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/profiles/profileEntity.kt b/src/main/kotlin/com/coded/spring/ordering/profiles/profileEntity.kt new file mode 100644 index 0000000..6ec4759 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/profiles/profileEntity.kt @@ -0,0 +1,19 @@ +package com.coded.spring.ordering.profiles + +import com.coded.spring.ordering.UserEntity +import jakarta.persistence.* + +@Entity +@Table(name = "profiles") +data class ProfileEntity( + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, + + @OneToOne + @JoinColumn(name = "user_id", referencedColumnName = "id") + var user: UserEntity? = null, + + var firstName: String = "", + var lastName: String = "", + var phoneNumber: String = "" +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/profiles/profileRepository.kt b/src/main/kotlin/com/coded/spring/ordering/profiles/profileRepository.kt new file mode 100644 index 0000000..97a7cdb --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/profiles/profileRepository.kt @@ -0,0 +1,7 @@ +package com.coded.spring.ordering.profiles + +import jakarta.inject.Named +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface ProfileRepository : JpaRepository From 9141963cb72e3e82be7781074d6e18d2e2d00c23 Mon Sep 17 00:00:00 2001 From: Zainab Date: Mon, 21 Apr 2025 11:40:57 +0300 Subject: [PATCH 06/11] User Authentication Using JWT --- pom.xml | 17 +++++++ .../ordering/AuthenticationController.kt | 38 ++++++++++++++++ .../coded/spring/ordering/SecurityConfig.kt | 9 +++- .../spring/ordering/TalabatController.kt | 26 ++++++++--- .../spring/ordering/customUserDetails.kt | 21 +++++++++ .../ordering/jwtAuthenticationFilter.kt | 45 +++++++++++++++++++ .../com/coded/spring/ordering/jwtService.kt | 42 +++++++++++++++++ .../spring/ordering/orders/OrdersService.kt | 2 +- 8 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/ordering/AuthenticationController.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/customUserDetails.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/jwtAuthenticationFilter.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/jwtService.kt diff --git a/pom.xml b/pom.xml index 43953f3..cf70251 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,23 @@ com.fasterxml.jackson.core jackson-databind + + 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/src/main/kotlin/com/coded/spring/ordering/AuthenticationController.kt b/src/main/kotlin/com/coded/spring/ordering/AuthenticationController.kt new file mode 100644 index 0000000..6a5758e --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/AuthenticationController.kt @@ -0,0 +1,38 @@ +package com.coded.spring.ordering + +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("/authentication") +class AuthenticationController( + private val authenticationManager: AuthenticationManager, + private val userDetailsService: UserDetailsService, + private val jwtService: JwtService +) { + + @PostMapping("/login") + fun login(@RequestBody request: AuthenticationRequest): AuthenticationResponse { + val authenticationToken = UsernamePasswordAuthenticationToken(request.username, request.password) + val authentication = authenticationManager.authenticate(authenticationToken) + + if (authentication.isAuthenticated) { + val userDetails = userDetailsService.loadUserByUsername(request.username) + val token = jwtService.generateToken(userDetails.username) + return AuthenticationResponse(token) + } else { + throw UsernameNotFoundException("Invalid login") + } + } +} + +data class AuthenticationRequest( + val username: String, + val password: String +) + +data class AuthenticationResponse( + val token: String +) diff --git a/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt index eb071fa..b81b231 100644 --- a/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt +++ b/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt @@ -2,6 +2,8 @@ package com.coded.spring.ordering import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.security.authentication.AuthenticationManager +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.core.userdetails.UserDetailsService @@ -18,6 +20,11 @@ class SecurityConfig( @Bean fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder() + @Bean + fun authenticationManager(authConfig: AuthenticationConfiguration): AuthenticationManager { + return authConfig.authenticationManager + } + @Bean fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { http.csrf { it.disable() } @@ -29,4 +36,4 @@ class SecurityConfig( .httpBasic {} return http.build() } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt index f7ae218..1ad5cc4 100644 --- a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt @@ -3,6 +3,8 @@ package com.coded.spring.ordering import org.springframework.web.bind.annotation.* import org.springframework.beans.factory.annotation.Value import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.security.core.userdetails.UserDetails @RestController class TalabatController( @@ -26,15 +28,27 @@ class TalabatController( ) ) } + + @GetMapping("/talabat-menu") + fun menu(): List { + return listOf("Pizza", "Burger", "Sushi", "Pasta") + } + + @PostMapping("/order") + fun submitOrder( + @RequestBody request: OrderRequest, + @AuthenticationPrincipal user: UserDetails + ): String { + return "Order received from ${user.username} for items: ${request.items.joinToString(", ")}" + } } data class ClientsRequest( val name: String, val username: String, - val password: String) -// val restaurant: String, -// val items:List) - - - + val password: String +) +data class OrderRequest( + val items: List +) diff --git a/src/main/kotlin/com/coded/spring/ordering/customUserDetails.kt b/src/main/kotlin/com/coded/spring/ordering/customUserDetails.kt new file mode 100644 index 0000000..933e181 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/customUserDetails.kt @@ -0,0 +1,21 @@ +//package com.coded.spring.ordering +// +//import org.springframework.security.core.GrantedAuthority +//import org.springframework.security.core.authority.SimpleGrantedAuthority +//import org.springframework.security.core.userdetails.UserDetails +// +//data class CustomUserDetails( +// val id: Long, +// private val username: String, +// private val password: String +//) : UserDetails { +// override fun getAuthorities(): Collection = +// listOf(SimpleGrantedAuthority("ROLE_USER")) +// +// override fun getPassword(): String = password +// override fun getUsername(): String = username +// override fun isAccountNonExpired(): Boolean = true +// override fun isAccountNonLocked(): Boolean = true +// override fun isCredentialsNonExpired(): Boolean = true +// override fun isEnabled(): Boolean = true +//} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/jwtAuthenticationFilter.kt b/src/main/kotlin/com/coded/spring/ordering/jwtAuthenticationFilter.kt new file mode 100644 index 0000000..0dde440 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/jwtAuthenticationFilter.kt @@ -0,0 +1,45 @@ +package com.coded.spring.ordering + +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 + +@Component +class JwtAuthenticationFilter( + private val jwtService: JwtService, + 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 = jwtService.extractUsername(token) + + if (SecurityContextHolder.getContext().authentication == null) { + if (jwtService.isTokenValid(token, username)) { + val userDetails = userDetailsService.loadUserByUsername(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/jwtService.kt b/src/main/kotlin/com/coded/spring/ordering/jwtService.kt new file mode 100644 index 0000000..d137a3d --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/jwtService.kt @@ -0,0 +1,42 @@ +package com.coded.spring.ordering + +import io.jsonwebtoken.* +import io.jsonwebtoken.security.Keys +import org.springframework.stereotype.Component +import java.util.* +import javax.crypto.SecretKey + +@Component +class JwtService { + + private val secretKey: SecretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256) + private val expirationMs: Long = 1000 * 60 * 60 + + 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/orders/OrdersService.kt b/src/main/kotlin/com/coded/spring/ordering/orders/OrdersService.kt index 5d77131..4a502de 100644 --- a/src/main/kotlin/com/coded/spring/ordering/orders/OrdersService.kt +++ b/src/main/kotlin/com/coded/spring/ordering/orders/OrdersService.kt @@ -26,4 +26,4 @@ class OrdersService( fun listOrders(): List { return orderRepository.findAll() } -} +} \ No newline at end of file From 5ea445c39bc1a17de02f7e57586e323f11d10043 Mon Sep 17 00:00:00 2001 From: Zainab Date: Tue, 29 Apr 2025 18:07:59 +0300 Subject: [PATCH 07/11] Profiles task done - Test was added --- pom.xml | 12 +++ .../spring/ordering/TalabatRepository.kt | 1 - .../coded/spring/ordering/ApplicationTests.kt | 89 +++++++++++++++++-- src/test/resources/application-test.yml | 17 ++++ 4 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 src/test/resources/application-test.yml diff --git a/pom.xml b/pom.xml index cf70251..8d5fd0c 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,7 @@ org.postgresql postgresql + runtime jakarta.inject @@ -63,11 +64,22 @@ com.h2database h2 + test org.jetbrains.kotlin kotlin-stdlib + + org.springframework.boot + spring-boot-starter-test + test + + + org.jetbrains.kotlin + kotlin-test-junit5 + test + com.fasterxml.jackson.core jackson-databind diff --git a/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt b/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt index 642bab1..18b2035 100644 --- a/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt +++ b/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt @@ -4,7 +4,6 @@ import jakarta.inject.Named import jakarta.persistence.* import org.springframework.data.jpa.repository.JpaRepository -@Named interface TalabatRepository : JpaRepository{ fun findByUsername(username: String): UserEntity? } diff --git a/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt b/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt index b2e2320..6a1c4ab 100644 --- a/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt +++ b/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt @@ -1,13 +1,92 @@ package com.coded.spring.ordering +import com.coded.spring.ordering.orders.OrderRepository +import com.coded.spring.ordering.profiles.ProfileEntity +import com.coded.spring.ordering.profiles.ProfileRepository +import com.coded.spring.ordering.profiles.ProfileRequest +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.client.TestRestTemplate +import org.springframework.boot.test.web.server.LocalServerPort +import org.springframework.http.HttpEntity +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.test.context.ActiveProfiles -@SpringBootTest -class ApplicationTests { +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles("test") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class ProfileControllerTest { - @Test - fun contextLoads() { + @LocalServerPort + var port: Int = 0 + + @Autowired + lateinit var usersRepository: TalabatRepository + + @Autowired + lateinit var ordersRepository: OrderRepository + + @Autowired + lateinit var profileRepository: ProfileRepository + + @Autowired + lateinit var passwordEncoder: PasswordEncoder + + @Autowired + lateinit var restTemplate: TestRestTemplate + + lateinit var tokenUsername: String + lateinit var tokenPassword: String + + @BeforeAll + fun setUp() { + val username = "ZainabAlS" + val rawPassword = "joincoded" + val existingUser = usersRepository.findByUsername(username) + + if (existingUser == null) { + val testUser = UserEntity( + name = "Zainab", + username = username, + password = passwordEncoder.encode(rawPassword) + ) + usersRepository.save(testUser) + } + + tokenUsername = username + tokenPassword = rawPassword } -} + @Test + fun `authenticated user can create profile`() { + val request = ProfileRequest( + firstName = "Zainab", + lastName = "AlSaffar", + phoneNumber = "12345678" + ) + + val headers = HttpHeaders().apply { + contentType = MediaType.APPLICATION_JSON + setBasicAuth(tokenUsername, tokenPassword) + } + + val entity = HttpEntity(request, headers) + val response = restTemplate.postForEntity( + "http://localhost:$port/profile", + entity, + ProfileEntity::class.java + ) + + assertEquals(HttpStatus.OK, response.statusCode) + assertNotNull(response.body) + assertEquals("Zainab", response.body?.firstName) + assertEquals("AlSaffar", response.body?.lastName) + } +} \ No newline at end of file diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml new file mode 100644 index 0000000..ff4bdee --- /dev/null +++ b/src/test/resources/application-test.yml @@ -0,0 +1,17 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1 + driver-class-name: org.h2.Driver + username: sa + password: + jpa: + hibernate: + ddl-auto: create-drop + show-sql: true + properties: + hibernate: + dialect: org.hibernate.dialect.H2Dialect + +sql: +init: +mode: never # Avoids running schema.sql/data.sql if not needed From 50a0609f00b757f3f36386cc29a7b41121fe1542 Mon Sep 17 00:00:00 2001 From: Zainab Date: Fri, 2 May 2025 17:35:33 +0300 Subject: [PATCH 08/11] Problems were fixed and menu was cached --- pom.xml | 33 +++++++++++++++ .../coded/spring/ordering/SecurityConfig.kt | 26 +++++++++--- .../ordering/cache/hazelCastInstance.kt | 11 +++++ .../spring/ordering/customUserDetails.kt | 42 +++++++++---------- .../coded/spring/ordering/menu/menuCache.kt | 5 +++ .../coded/spring/ordering/menu/menuService.kt | 19 +++++++-- .../coded/spring/ordering/ApplicationTests.kt | 22 ++++++++-- src/test/resources/application-test.yml | 2 +- 8 files changed, 126 insertions(+), 34 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/ordering/cache/hazelCastInstance.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/menu/menuCache.kt diff --git a/pom.xml b/pom.xml index 8d5fd0c..98a2368 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,12 @@ org.jetbrains.kotlin kotlin-reflect + + com.hazelcast + hazelcast + 5.5.0 + + org.postgresql postgresql @@ -52,6 +58,20 @@ jakarta.inject jakarta.inject-api + + io.cucumber + cucumber-java + 7.20.1 + test + + + + + io.cucumber + cucumber-spring + 7.14.0 + test + org.springframework.boot @@ -61,6 +81,19 @@ org.springframework.boot spring-boot-starter-data-jpa + + io.cucumber + cucumber-junit-platform-engine + 7.20.1 + test + + + org.junit.platform + junit-platform-suite-api + 1.10.0 + test + + com.h2database h2 diff --git a/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt index b81b231..d00f353 100644 --- a/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt +++ b/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt @@ -6,15 +6,18 @@ import org.springframework.security.authentication.AuthenticationManager 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 userDetailsService: UserDetailsService + private val userDetailsService: UserDetailsService, + private val jwtAuthenticationFilter: JwtAuthenticationFilter ) { @Bean @@ -27,13 +30,26 @@ class SecurityConfig( @Bean fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - http.csrf { it.disable() } + http + .csrf { it.disable() } .authorizeHttpRequests { - it.requestMatchers("/menu", "/clients", "/welcome").permitAll() + it.requestMatchers("/menu", "/clients", "/welcome", "/authentication/**").permitAll() it.anyRequest().authenticated() } - .formLogin { it.defaultSuccessUrl("/welcome", true) } - .httpBasic {} + .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) } + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java) + .httpBasic { it.disable() } + .formLogin { it.disable() } + return http.build() } +// http.csrf { it.disable() } +// .authorizeHttpRequests { +// it.requestMatchers("/menu", "/clients", "/welcome").permitAll() +// it.anyRequest().authenticated() +// } +// .formLogin { it.defaultSuccessUrl("/welcome", true) } +// .httpBasic {} +// return http.build() +// } } diff --git a/src/main/kotlin/com/coded/spring/ordering/cache/hazelCastInstance.kt b/src/main/kotlin/com/coded/spring/ordering/cache/hazelCastInstance.kt new file mode 100644 index 0000000..18cce05 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/cache/hazelCastInstance.kt @@ -0,0 +1,11 @@ +package com.coded.spring.ordering.cache + +import com.hazelcast.config.Config +import com.hazelcast.core.Hazelcast +import com.hazelcast.core.HazelcastInstance + +val menuCacheConfig = Config("menu-cache").apply { + getMapConfig("menu").timeToLiveSeconds = 60 +} + +val menuHazelcastInstance: HazelcastInstance = Hazelcast.newHazelcastInstance(menuCacheConfig) diff --git a/src/main/kotlin/com/coded/spring/ordering/customUserDetails.kt b/src/main/kotlin/com/coded/spring/ordering/customUserDetails.kt index 933e181..14b5131 100644 --- a/src/main/kotlin/com/coded/spring/ordering/customUserDetails.kt +++ b/src/main/kotlin/com/coded/spring/ordering/customUserDetails.kt @@ -1,21 +1,21 @@ -//package com.coded.spring.ordering -// -//import org.springframework.security.core.GrantedAuthority -//import org.springframework.security.core.authority.SimpleGrantedAuthority -//import org.springframework.security.core.userdetails.UserDetails -// -//data class CustomUserDetails( -// val id: Long, -// private val username: String, -// private val password: String -//) : UserDetails { -// override fun getAuthorities(): Collection = -// listOf(SimpleGrantedAuthority("ROLE_USER")) -// -// override fun getPassword(): String = password -// override fun getUsername(): String = username -// override fun isAccountNonExpired(): Boolean = true -// override fun isAccountNonLocked(): Boolean = true -// override fun isCredentialsNonExpired(): Boolean = true -// override fun isEnabled(): Boolean = true -//} \ No newline at end of file +package com.coded.spring.ordering + +import org.springframework.security.core.GrantedAuthority +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.core.userdetails.UserDetails + +data class CustomUserDetails( + val id: Long, + private val username: String, + private val password: String +) : UserDetails { + override fun getAuthorities(): Collection = + listOf(SimpleGrantedAuthority("ROLE_USER")) + + override fun getPassword(): String = password + override fun getUsername(): String = username + override fun isAccountNonExpired(): Boolean = true + override fun isAccountNonLocked(): Boolean = true + override fun isCredentialsNonExpired(): Boolean = true + override fun isEnabled(): Boolean = true +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuCache.kt b/src/main/kotlin/com/coded/spring/ordering/menu/menuCache.kt new file mode 100644 index 0000000..c62bb76 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/menu/menuCache.kt @@ -0,0 +1,5 @@ +package com.coded.spring.ordering.menu + +import com.coded.spring.ordering.cache.menuHazelcastInstance + +val menuCache = menuHazelcastInstance.getMap>("menu") diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt b/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt index 288c851..6acf606 100644 --- a/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt +++ b/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt @@ -1,20 +1,33 @@ package com.coded.spring.ordering.menu +import com.coded.spring.ordering.cache.menuHazelcastInstance import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Service + @Service class MenuService(private val menuRepository: MenuRepository) { - @Cacheable("menu") fun getMenu(): List { + val cached = menuCache["all"] + if (cached != null) { + println("Returning menu from cache") + return cached + } println("Fetching menu from DB...") - return menuRepository.findAll() + val menu = menuRepository.findAll() + menuCache["all"] = menu + return menu } fun addMenuItem(request: MenuRequest): MenuEntity { val newItem = MenuEntity(name = request.name, price = request.price) - return menuRepository.save(newItem) + val savedItem = menuRepository.save(newItem) + + menuCache.remove("all") + println("Cleared menu cache after adding new item") + + return savedItem } } \ No newline at end of file diff --git a/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt b/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt index 6a1c4ab..45a40f8 100644 --- a/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt +++ b/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt @@ -45,13 +45,14 @@ class ProfileControllerTest { lateinit var tokenUsername: String lateinit var tokenPassword: String + lateinit var token: String + @BeforeAll fun setUp() { val username = "ZainabAlS" val rawPassword = "joincoded" - val existingUser = usersRepository.findByUsername(username) - if (existingUser == null) { + if (usersRepository.findByUsername(username) == null) { val testUser = UserEntity( name = "Zainab", username = username, @@ -60,10 +61,21 @@ class ProfileControllerTest { usersRepository.save(testUser) } + val loginRequest = AuthenticationRequest(username, rawPassword) + val loginResponse = restTemplate.postForEntity( + "http://localhost:$port/authentication/login", + loginRequest, + AuthenticationResponse::class.java + ) + + assertEquals(HttpStatus.OK, loginResponse.statusCode) + token = loginResponse.body?.token ?: throw IllegalStateException("Token was not returned") + tokenUsername = username tokenPassword = rawPassword } + @Test fun `authenticated user can create profile`() { val request = ProfileRequest( @@ -74,10 +86,11 @@ class ProfileControllerTest { val headers = HttpHeaders().apply { contentType = MediaType.APPLICATION_JSON - setBasicAuth(tokenUsername, tokenPassword) + setBearerAuth(token) } val entity = HttpEntity(request, headers) + val response = restTemplate.postForEntity( "http://localhost:$port/profile", entity, @@ -89,4 +102,5 @@ class ProfileControllerTest { assertEquals("Zainab", response.body?.firstName) assertEquals("AlSaffar", response.body?.lastName) } -} \ No newline at end of file + +} diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index ff4bdee..db57135 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -14,4 +14,4 @@ spring: sql: init: -mode: never # Avoids running schema.sql/data.sql if not needed +mode: never \ No newline at end of file From 37d1b0a5143a63fdc966782733ec57630ca7a8a4 Mon Sep 17 00:00:00 2001 From: Zainab Date: Fri, 2 May 2025 18:07:27 +0300 Subject: [PATCH 09/11] Configuration task complete --- .../com/coded/spring/ordering/Application.kt | 2 ++ .../coded/spring/ordering/SecurityConfig.kt | 9 ------ .../spring/ordering/TalabatController.kt | 22 ++++++++------ .../coded/spring/ordering/appConfiguration.kt | 16 ++++++++++ .../coded/spring/ordering/menu/menuService.kt | 30 ++++++++++++------- src/main/resources/application.properties | 9 +++++- 6 files changed, 59 insertions(+), 29 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/ordering/appConfiguration.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/Application.kt b/src/main/kotlin/com/coded/spring/ordering/Application.kt index 8554e49..590d1ce 100644 --- a/src/main/kotlin/com/coded/spring/ordering/Application.kt +++ b/src/main/kotlin/com/coded/spring/ordering/Application.kt @@ -1,9 +1,11 @@ package com.coded.spring.ordering import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.runApplication @SpringBootApplication +@EnableConfigurationProperties(AppProperties::class) class Application fun main(args: Array) { diff --git a/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt index d00f353..e71d5aa 100644 --- a/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt +++ b/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt @@ -43,13 +43,4 @@ class SecurityConfig( return http.build() } -// http.csrf { it.disable() } -// .authorizeHttpRequests { -// it.requestMatchers("/menu", "/clients", "/welcome").permitAll() -// it.anyRequest().authenticated() -// } -// .formLogin { it.defaultSuccessUrl("/welcome", true) } -// .httpBasic {} -// return http.build() -// } } diff --git a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt index 1ad5cc4..7dfd818 100644 --- a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt @@ -1,7 +1,6 @@ package com.coded.spring.ordering import org.springframework.web.bind.annotation.* -import org.springframework.beans.factory.annotation.Value import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.security.core.userdetails.UserDetails @@ -10,12 +9,17 @@ import org.springframework.security.core.userdetails.UserDetails class TalabatController( private val talabatRepository: TalabatRepository, private val passwordEncoder: PasswordEncoder, - @Value("\${server-welcome-message}") - val welcomeMessage: String + private val appProperties: AppProperties ) { @GetMapping("/welcome") - fun welcomeToTalabat() = "Hello! $welcomeMessage" + fun welcomeToTalabat(): String { + return if (appProperties.festive.enabled) { + appProperties.festive.message + } else { + "Welcome to Online Ordering by ${appProperties.companyName}" + } + } @PostMapping("/clients") fun clients(@RequestBody request: ClientsRequest): UserEntity { @@ -29,11 +33,6 @@ class TalabatController( ) } - @GetMapping("/talabat-menu") - fun menu(): List { - return listOf("Pizza", "Burger", "Sushi", "Pasta") - } - @PostMapping("/order") fun submitOrder( @RequestBody request: OrderRequest, @@ -52,3 +51,8 @@ data class ClientsRequest( data class OrderRequest( val items: List ) + +data class MenuItem( + val name: String, + val price: Double +) diff --git a/src/main/kotlin/com/coded/spring/ordering/appConfiguration.kt b/src/main/kotlin/com/coded/spring/ordering/appConfiguration.kt new file mode 100644 index 0000000..d17ab95 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/appConfiguration.kt @@ -0,0 +1,16 @@ +package com.coded.spring.ordering + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.context.annotation.Configuration + +@Configuration +@ConfigurationProperties(prefix = "app") +class AppProperties { + lateinit var companyName: String + var festive: Festive = Festive() + + class Festive { + var enabled: Boolean = false + var message: String = "" + } +} diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt b/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt index 6acf606..e01a674 100644 --- a/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt +++ b/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt @@ -1,33 +1,43 @@ package com.coded.spring.ordering.menu +import com.coded.spring.ordering.AppProperties import com.coded.spring.ordering.cache.menuHazelcastInstance import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Service @Service -class MenuService(private val menuRepository: MenuRepository) { +class MenuService( + private val menuRepository: MenuRepository, + private val appProperties: AppProperties +) { fun getMenu(): List { val cached = menuCache["all"] - if (cached != null) { + val menu = if (cached != null) { println("Returning menu from cache") - return cached + cached + } else { + println("Fetching menu from DB...") + val dbMenu = menuRepository.findAll() + menuCache["all"] = dbMenu + dbMenu + } + + return if (appProperties.festive.enabled) { + menu.map { it.copy(price = (it.price * 0.8)) } + } else { + menu } - println("Fetching menu from DB...") - val menu = menuRepository.findAll() - menuCache["all"] = menu - return menu } fun addMenuItem(request: MenuRequest): MenuEntity { val newItem = MenuEntity(name = request.name, price = request.price) - val savedItem = menuRepository.save(newItem) + val saved = menuRepository.save(newItem) menuCache.remove("all") println("Cleared menu cache after adding new item") - return savedItem + return saved } - } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e16c324..4681f50 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,4 +4,11 @@ spring.datasource.url=jdbc:postgresql://localhost:5432/talabatDB spring.datasource.username=postgres spring.datasource.password=NBKPostgres spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect -server-welcome-message=Welcome to Talabat! Where all your favourite restaurants are a click away \ No newline at end of file + +#server-welcome-message=Welcome to Talabat! Where all your favourite restaurants are a click away + +server-welcome-message=Welcome to Online Ordering by ${app.company-name} + +app.company-name=Zainab Foods +app.festive.enabled=true +app.festive.message=Eidkom Mubarak \ No newline at end of file From 4e56593aba626a03312074115abf8ad11f130f6f Mon Sep 17 00:00:00 2001 From: Zainab Date: Sat, 3 May 2025 00:58:56 +0300 Subject: [PATCH 10/11] Basic Swagger was implemented --- pom.xml | 6 +++++- src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt | 2 +- src/main/resources/application.properties | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 98a2368..f59d09b 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,11 @@ hazelcast 5.5.0 - + + org.springdoc + springdoc-openapi-starter-webmvc-api + 2.6.0 + org.postgresql postgresql diff --git a/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt index e71d5aa..79a6633 100644 --- a/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt +++ b/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt @@ -33,7 +33,7 @@ class SecurityConfig( http .csrf { it.disable() } .authorizeHttpRequests { - it.requestMatchers("/menu", "/clients", "/welcome", "/authentication/**").permitAll() + it.requestMatchers("/menu", "/clients", "/welcome", "/authentication/**","/api-docs").permitAll() it.anyRequest().authenticated() } .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4681f50..a7bf29c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -11,4 +11,6 @@ server-welcome-message=Welcome to Online Ordering by ${app.company-name} app.company-name=Zainab Foods app.festive.enabled=true -app.festive.message=Eidkom Mubarak \ No newline at end of file +app.festive.message=Eidkom Mubarak + +springdoc.api-docs.path=/api-docs \ No newline at end of file From f2dbffc6cbd943f303bbf2bb4d18652fd6c45490 Mon Sep 17 00:00:00 2001 From: Zainab Date: Tue, 13 May 2025 17:07:15 +0300 Subject: [PATCH 11/11] added the json file and seperated into different modules --- auth/pom.xml | 92 ++++++++ .../kotlin/auth-service/AuthApplication.kt | 11 + .../auth-service}/AuthenticationController.kt | 2 +- .../kotlin/auth-service}/SecurityConfig.kt | 2 +- .../kotlin/auth-service}/TalabatRepository.kt | 3 +- .../auth-service}/UserDetailsService.kt | 3 +- .../kotlin/auth-service}/customUserDetails.kt | 2 +- .../auth-service}/jwtAuthenticationFilter.kt | 2 +- .../main/kotlin/auth-service}/jwtService.kt | 2 +- ordercenter/pom.xml | 90 ++++++++ .../ordering-service}/TalabatController.kt | 11 +- .../cache/hazelCastInstance.kt | 2 +- .../kotlin/ordering-service/menu/menuCache.kt | 5 + .../ordering-service}/menu/menuController.kt | 2 +- .../ordering-service}/menu/menuEntity.kt | 2 +- .../ordering-service}/menu/menuRepository.kt | 3 +- .../ordering-service}/menu/menuService.kt | 4 +- .../orders/CreateOrderRequest.kt | 2 +- .../ordering-service}/orders/ItemsEntity.kt | 3 +- .../orders/ItemsRepository.kt | 4 +- .../ordering-service}/orders/OrderEntity.kt | 3 +- .../orders/OrdersController.kt | 3 +- .../orders/OrdersRepository.kt | 4 +- .../ordering-service}/orders/OrdersService.kt | 6 +- .../profiles/profileController.kt | 2 +- .../profiles/profileEntity.kt | 2 +- .../profiles/profileRepository.kt | 3 +- pom.xml | 6 + .../coded/spring/ordering/menu/menuCache.kt | 5 - .../coded/spring/ordering/ApplicationTests.kt | 212 +++++++++--------- welcoming/pom.xml | 96 ++++++++ .../kotlin/welcome-service}/Application.kt | 2 +- .../welcome-service}/appConfiguration.kt | 2 +- .../resources/application.properties | 0 .../welcome-service/welcomeController.kt | 21 ++ ...saffar-online-ordering-api-swagger-01.json | 1 + 36 files changed, 461 insertions(+), 154 deletions(-) create mode 100644 auth/pom.xml create mode 100644 auth/src/main/kotlin/auth-service/AuthApplication.kt rename {src/main/kotlin/com/coded/spring/ordering => auth/src/main/kotlin/auth-service}/AuthenticationController.kt (97%) rename {src/main/kotlin/com/coded/spring/ordering => auth/src/main/kotlin/auth-service}/SecurityConfig.kt (98%) rename {src/main/kotlin/com/coded/spring/ordering => auth/src/main/kotlin/auth-service}/TalabatRepository.kt (89%) rename {src/main/kotlin/com/coded/spring/ordering => auth/src/main/kotlin/auth-service}/UserDetailsService.kt (90%) rename {src/main/kotlin/com/coded/spring/ordering => auth/src/main/kotlin/auth-service}/customUserDetails.kt (95%) rename {src/main/kotlin/com/coded/spring/ordering => auth/src/main/kotlin/auth-service}/jwtAuthenticationFilter.kt (98%) rename {src/main/kotlin/com/coded/spring/ordering => auth/src/main/kotlin/auth-service}/jwtService.kt (96%) create mode 100644 ordercenter/pom.xml rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/TalabatController.kt (81%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/cache/hazelCastInstance.kt (88%) create mode 100644 ordercenter/src/main/kotlin/ordering-service/menu/menuCache.kt rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/menu/menuController.kt (91%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/menu/menuEntity.kt (85%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/menu/menuRepository.kt (62%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/menu/menuService.kt (87%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/orders/CreateOrderRequest.kt (82%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/orders/ItemsEntity.kt (74%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/orders/ItemsRepository.kt (57%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/orders/OrderEntity.kt (81%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/orders/OrdersController.kt (81%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/orders/OrdersRepository.kt (54%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/orders/OrdersService.kt (82%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/profiles/profileController.kt (96%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/profiles/profileEntity.kt (90%) rename {src/main/kotlin/com/coded/spring/ordering => ordercenter/src/main/kotlin/ordering-service}/profiles/profileRepository.kt (65%) delete mode 100644 src/main/kotlin/com/coded/spring/ordering/menu/menuCache.kt create mode 100644 welcoming/pom.xml rename {src/main/kotlin/com/coded/spring/ordering => welcoming/src/main/kotlin/welcome-service}/Application.kt (89%) rename {src/main/kotlin/com/coded/spring/ordering => welcoming/src/main/kotlin/welcome-service}/appConfiguration.kt (90%) rename {src/main => welcoming/src/main/kotlin/welcome-service}/resources/application.properties (100%) create mode 100644 welcoming/src/main/kotlin/welcome-service/welcomeController.kt create mode 100644 zainab-alsaffar-online-ordering-api-swagger-01.json diff --git a/auth/pom.xml b/auth/pom.xml new file mode 100644 index 0000000..547c47f --- /dev/null +++ b/auth/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + com.coded.spring + Ordering + 0.0.1-SNAPSHOT + + + auth + + + UTF-8 + official + 1.8 + + + + + mavenCentral + https://repo1.maven.org/maven2/ + + + + + src/main/kotlin + src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + maven-surefire-plugin + 2.22.2 + + + maven-failsafe-plugin + 2.22.2 + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + MainKt + + + + + + + + org.jetbrains.kotlin + kotlin-test-junit5 + 2.1.0 + test + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + org.jetbrains.kotlin + kotlin-stdlib + 2.1.0 + + + + + + diff --git a/auth/src/main/kotlin/auth-service/AuthApplication.kt b/auth/src/main/kotlin/auth-service/AuthApplication.kt new file mode 100644 index 0000000..fb67b5c --- /dev/null +++ b/auth/src/main/kotlin/auth-service/AuthApplication.kt @@ -0,0 +1,11 @@ +package `auth-service` + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication + +@SpringBootApplication +class AuthApplication + +fun main(args: Array) { + runApplication(*args) +} diff --git a/src/main/kotlin/com/coded/spring/ordering/AuthenticationController.kt b/auth/src/main/kotlin/auth-service/AuthenticationController.kt similarity index 97% rename from src/main/kotlin/com/coded/spring/ordering/AuthenticationController.kt rename to auth/src/main/kotlin/auth-service/AuthenticationController.kt index 6a5758e..66eb4cb 100644 --- a/src/main/kotlin/com/coded/spring/ordering/AuthenticationController.kt +++ b/auth/src/main/kotlin/auth-service/AuthenticationController.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering +package `auth-service` import org.springframework.security.authentication.* import org.springframework.security.core.userdetails.UserDetailsService diff --git a/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt b/auth/src/main/kotlin/auth-service/SecurityConfig.kt similarity index 98% rename from src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt rename to auth/src/main/kotlin/auth-service/SecurityConfig.kt index 79a6633..4546bbc 100644 --- a/src/main/kotlin/com/coded/spring/ordering/SecurityConfig.kt +++ b/auth/src/main/kotlin/auth-service/SecurityConfig.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering +package `auth-service` import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration diff --git a/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt b/auth/src/main/kotlin/auth-service/TalabatRepository.kt similarity index 89% rename from src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt rename to auth/src/main/kotlin/auth-service/TalabatRepository.kt index 18b2035..5a16427 100644 --- a/src/main/kotlin/com/coded/spring/ordering/TalabatRepository.kt +++ b/auth/src/main/kotlin/auth-service/TalabatRepository.kt @@ -1,6 +1,5 @@ -package com.coded.spring.ordering +package `auth-service` -import jakarta.inject.Named import jakarta.persistence.* import org.springframework.data.jpa.repository.JpaRepository diff --git a/src/main/kotlin/com/coded/spring/ordering/UserDetailsService.kt b/auth/src/main/kotlin/auth-service/UserDetailsService.kt similarity index 90% rename from src/main/kotlin/com/coded/spring/ordering/UserDetailsService.kt rename to auth/src/main/kotlin/auth-service/UserDetailsService.kt index 2f5c79b..8320cbb 100644 --- a/src/main/kotlin/com/coded/spring/ordering/UserDetailsService.kt +++ b/auth/src/main/kotlin/auth-service/UserDetailsService.kt @@ -1,6 +1,7 @@ -package com.coded.spring.ordering +package `auth-service` +import `auth-service`.TalabatRepository import org.springframework.security.core.userdetails.* import org.springframework.stereotype.Service diff --git a/src/main/kotlin/com/coded/spring/ordering/customUserDetails.kt b/auth/src/main/kotlin/auth-service/customUserDetails.kt similarity index 95% rename from src/main/kotlin/com/coded/spring/ordering/customUserDetails.kt rename to auth/src/main/kotlin/auth-service/customUserDetails.kt index 14b5131..e58e7ea 100644 --- a/src/main/kotlin/com/coded/spring/ordering/customUserDetails.kt +++ b/auth/src/main/kotlin/auth-service/customUserDetails.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering +package `auth-service` import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.authority.SimpleGrantedAuthority diff --git a/src/main/kotlin/com/coded/spring/ordering/jwtAuthenticationFilter.kt b/auth/src/main/kotlin/auth-service/jwtAuthenticationFilter.kt similarity index 98% rename from src/main/kotlin/com/coded/spring/ordering/jwtAuthenticationFilter.kt rename to auth/src/main/kotlin/auth-service/jwtAuthenticationFilter.kt index 0dde440..0cc5bc9 100644 --- a/src/main/kotlin/com/coded/spring/ordering/jwtAuthenticationFilter.kt +++ b/auth/src/main/kotlin/auth-service/jwtAuthenticationFilter.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering +package `auth-service` import jakarta.servlet.FilterChain import jakarta.servlet.http.* diff --git a/src/main/kotlin/com/coded/spring/ordering/jwtService.kt b/auth/src/main/kotlin/auth-service/jwtService.kt similarity index 96% rename from src/main/kotlin/com/coded/spring/ordering/jwtService.kt rename to auth/src/main/kotlin/auth-service/jwtService.kt index d137a3d..259ec02 100644 --- a/src/main/kotlin/com/coded/spring/ordering/jwtService.kt +++ b/auth/src/main/kotlin/auth-service/jwtService.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering +package `auth-service` import io.jsonwebtoken.* import io.jsonwebtoken.security.Keys diff --git a/ordercenter/pom.xml b/ordercenter/pom.xml new file mode 100644 index 0000000..3448249 --- /dev/null +++ b/ordercenter/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + com.coded.spring + Ordering + 0.0.1-SNAPSHOT + + + ordercenter + + + UTF-8 + official + 1.8 + + + + + mavenCentral + https://repo1.maven.org/maven2/ + + + + + src/main/kotlin + src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + maven-surefire-plugin + 2.22.2 + + + maven-failsafe-plugin + 2.22.2 + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + MainKt + + + + + + + + org.jetbrains.kotlin + kotlin-test-junit5 + 2.1.0 + test + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + org.jetbrains.kotlin + kotlin-stdlib + 2.1.0 + + + + \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt b/ordercenter/src/main/kotlin/ordering-service/TalabatController.kt similarity index 81% rename from src/main/kotlin/com/coded/spring/ordering/TalabatController.kt rename to ordercenter/src/main/kotlin/ordering-service/TalabatController.kt index 7dfd818..8ebbc79 100644 --- a/src/main/kotlin/com/coded/spring/ordering/TalabatController.kt +++ b/ordercenter/src/main/kotlin/ordering-service/TalabatController.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering +package `ordering-service` import org.springframework.web.bind.annotation.* import org.springframework.security.crypto.password.PasswordEncoder @@ -12,15 +12,6 @@ class TalabatController( private val appProperties: AppProperties ) { - @GetMapping("/welcome") - fun welcomeToTalabat(): String { - return if (appProperties.festive.enabled) { - appProperties.festive.message - } else { - "Welcome to Online Ordering by ${appProperties.companyName}" - } - } - @PostMapping("/clients") fun clients(@RequestBody request: ClientsRequest): UserEntity { val encodedPassword = passwordEncoder.encode(request.password) diff --git a/src/main/kotlin/com/coded/spring/ordering/cache/hazelCastInstance.kt b/ordercenter/src/main/kotlin/ordering-service/cache/hazelCastInstance.kt similarity index 88% rename from src/main/kotlin/com/coded/spring/ordering/cache/hazelCastInstance.kt rename to ordercenter/src/main/kotlin/ordering-service/cache/hazelCastInstance.kt index 18cce05..21251f9 100644 --- a/src/main/kotlin/com/coded/spring/ordering/cache/hazelCastInstance.kt +++ b/ordercenter/src/main/kotlin/ordering-service/cache/hazelCastInstance.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering.cache +package `ordering-service`.cache import com.hazelcast.config.Config import com.hazelcast.core.Hazelcast diff --git a/ordercenter/src/main/kotlin/ordering-service/menu/menuCache.kt b/ordercenter/src/main/kotlin/ordering-service/menu/menuCache.kt new file mode 100644 index 0000000..ac88c0f --- /dev/null +++ b/ordercenter/src/main/kotlin/ordering-service/menu/menuCache.kt @@ -0,0 +1,5 @@ +package `ordering-service`.menu + +import `ordering-service`.cache.menuHazelcastInstance + +val menuCache = menuHazelcastInstance.getMap>("menu") diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuController.kt b/ordercenter/src/main/kotlin/ordering-service/menu/menuController.kt similarity index 91% rename from src/main/kotlin/com/coded/spring/ordering/menu/menuController.kt rename to ordercenter/src/main/kotlin/ordering-service/menu/menuController.kt index 38abdb4..fedb874 100644 --- a/src/main/kotlin/com/coded/spring/ordering/menu/menuController.kt +++ b/ordercenter/src/main/kotlin/ordering-service/menu/menuController.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering.menu +package `ordering-service`.menu import org.springframework.web.bind.annotation.* diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuEntity.kt b/ordercenter/src/main/kotlin/ordering-service/menu/menuEntity.kt similarity index 85% rename from src/main/kotlin/com/coded/spring/ordering/menu/menuEntity.kt rename to ordercenter/src/main/kotlin/ordering-service/menu/menuEntity.kt index 6adeb95..5cfce72 100644 --- a/src/main/kotlin/com/coded/spring/ordering/menu/menuEntity.kt +++ b/ordercenter/src/main/kotlin/ordering-service/menu/menuEntity.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering.menu +package `ordering-service`.menu import jakarta.persistence.* diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuRepository.kt b/ordercenter/src/main/kotlin/ordering-service/menu/menuRepository.kt similarity index 62% rename from src/main/kotlin/com/coded/spring/ordering/menu/menuRepository.kt rename to ordercenter/src/main/kotlin/ordering-service/menu/menuRepository.kt index f8ef99a..7c99400 100644 --- a/src/main/kotlin/com/coded/spring/ordering/menu/menuRepository.kt +++ b/ordercenter/src/main/kotlin/ordering-service/menu/menuRepository.kt @@ -1,5 +1,6 @@ -package com.coded.spring.ordering.menu +package `ordering-service`.menu +import `ordering-service`.menu.MenuEntity import org.springframework.data.jpa.repository.JpaRepository interface MenuRepository : JpaRepository \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt b/ordercenter/src/main/kotlin/ordering-service/menu/menuService.kt similarity index 87% rename from src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt rename to ordercenter/src/main/kotlin/ordering-service/menu/menuService.kt index e01a674..3052cf0 100644 --- a/src/main/kotlin/com/coded/spring/ordering/menu/menuService.kt +++ b/ordercenter/src/main/kotlin/ordering-service/menu/menuService.kt @@ -1,8 +1,6 @@ -package com.coded.spring.ordering.menu +package `ordering-service`.menu import com.coded.spring.ordering.AppProperties -import com.coded.spring.ordering.cache.menuHazelcastInstance -import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Service diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/CreateOrderRequest.kt b/ordercenter/src/main/kotlin/ordering-service/orders/CreateOrderRequest.kt similarity index 82% rename from src/main/kotlin/com/coded/spring/ordering/orders/CreateOrderRequest.kt rename to ordercenter/src/main/kotlin/ordering-service/orders/CreateOrderRequest.kt index 4797c75..21cc481 100644 --- a/src/main/kotlin/com/coded/spring/ordering/orders/CreateOrderRequest.kt +++ b/ordercenter/src/main/kotlin/ordering-service/orders/CreateOrderRequest.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering.orders +package `ordering-service`.orders data class CreateOrderRequest( val userId: Long, diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/ItemsEntity.kt b/ordercenter/src/main/kotlin/ordering-service/orders/ItemsEntity.kt similarity index 74% rename from src/main/kotlin/com/coded/spring/ordering/orders/ItemsEntity.kt rename to ordercenter/src/main/kotlin/ordering-service/orders/ItemsEntity.kt index f6cb177..75fafa9 100644 --- a/src/main/kotlin/com/coded/spring/ordering/orders/ItemsEntity.kt +++ b/ordercenter/src/main/kotlin/ordering-service/orders/ItemsEntity.kt @@ -1,6 +1,5 @@ -package com.coded.spring.ordering.orders +package `ordering-service`.orders -import com.coded.spring.ordering.orders.entities.OrderEntity import jakarta.persistence.* @Entity diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/ItemsRepository.kt b/ordercenter/src/main/kotlin/ordering-service/orders/ItemsRepository.kt similarity index 57% rename from src/main/kotlin/com/coded/spring/ordering/orders/ItemsRepository.kt rename to ordercenter/src/main/kotlin/ordering-service/orders/ItemsRepository.kt index 45f58fa..275998c 100644 --- a/src/main/kotlin/com/coded/spring/ordering/orders/ItemsRepository.kt +++ b/ordercenter/src/main/kotlin/ordering-service/orders/ItemsRepository.kt @@ -1,7 +1,7 @@ -package com.coded.spring.ordering.orders +package `ordering-service`.orders import org.springframework.data.jpa.repository.JpaRepository -import com.coded.spring.ordering.orders.ItemEntity +import `ordering-service`.orders.ItemEntity interface ItemsRepository : JpaRepository diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/OrderEntity.kt b/ordercenter/src/main/kotlin/ordering-service/orders/OrderEntity.kt similarity index 81% rename from src/main/kotlin/com/coded/spring/ordering/orders/OrderEntity.kt rename to ordercenter/src/main/kotlin/ordering-service/orders/OrderEntity.kt index 47a12ac..d0c97fc 100644 --- a/src/main/kotlin/com/coded/spring/ordering/orders/OrderEntity.kt +++ b/ordercenter/src/main/kotlin/ordering-service/orders/OrderEntity.kt @@ -1,7 +1,6 @@ -package com.coded.spring.ordering.orders.entities +package `ordering-service`.orders import com.coded.spring.ordering.UserEntity -import com.coded.spring.ordering.orders.ItemEntity import jakarta.persistence.* @Entity diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/OrdersController.kt b/ordercenter/src/main/kotlin/ordering-service/orders/OrdersController.kt similarity index 81% rename from src/main/kotlin/com/coded/spring/ordering/orders/OrdersController.kt rename to ordercenter/src/main/kotlin/ordering-service/orders/OrdersController.kt index 0cae779..2840f0e 100644 --- a/src/main/kotlin/com/coded/spring/ordering/orders/OrdersController.kt +++ b/ordercenter/src/main/kotlin/ordering-service/orders/OrdersController.kt @@ -1,6 +1,5 @@ -package com.coded.spring.ordering.orders +package `ordering-service`.orders -import com.coded.spring.ordering.orders.entities.OrderEntity import org.springframework.web.bind.annotation.* @RestController diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/OrdersRepository.kt b/ordercenter/src/main/kotlin/ordering-service/orders/OrdersRepository.kt similarity index 54% rename from src/main/kotlin/com/coded/spring/ordering/orders/OrdersRepository.kt rename to ordercenter/src/main/kotlin/ordering-service/orders/OrdersRepository.kt index 0b9e993..949b207 100644 --- a/src/main/kotlin/com/coded/spring/ordering/orders/OrdersRepository.kt +++ b/ordercenter/src/main/kotlin/ordering-service/orders/OrdersRepository.kt @@ -1,6 +1,6 @@ -package com.coded.spring.ordering.orders +package `ordering-service`.orders import org.springframework.data.jpa.repository.JpaRepository -import com.coded.spring.ordering.orders.entities.OrderEntity +import `ordering-service`.orders.OrderEntity interface OrderRepository : JpaRepository diff --git a/src/main/kotlin/com/coded/spring/ordering/orders/OrdersService.kt b/ordercenter/src/main/kotlin/ordering-service/orders/OrdersService.kt similarity index 82% rename from src/main/kotlin/com/coded/spring/ordering/orders/OrdersService.kt rename to ordercenter/src/main/kotlin/ordering-service/orders/OrdersService.kt index 4a502de..028091e 100644 --- a/src/main/kotlin/com/coded/spring/ordering/orders/OrdersService.kt +++ b/ordercenter/src/main/kotlin/ordering-service/orders/OrdersService.kt @@ -1,8 +1,10 @@ -package com.coded.spring.ordering.orders +package `ordering-service`.orders import com.coded.spring.ordering.TalabatRepository -import com.coded.spring.ordering.orders.entities.OrderEntity +import `ordering-service`.orders.OrderEntity import com.fasterxml.jackson.databind.ObjectMapper +import `ordering-service`.orders.CreateOrderRequest +import `ordering-service`.orders.OrderRepository import org.springframework.stereotype.Service @Service diff --git a/src/main/kotlin/com/coded/spring/ordering/profiles/profileController.kt b/ordercenter/src/main/kotlin/ordering-service/profiles/profileController.kt similarity index 96% rename from src/main/kotlin/com/coded/spring/ordering/profiles/profileController.kt rename to ordercenter/src/main/kotlin/ordering-service/profiles/profileController.kt index 65063aa..2fdc67c 100644 --- a/src/main/kotlin/com/coded/spring/ordering/profiles/profileController.kt +++ b/ordercenter/src/main/kotlin/ordering-service/profiles/profileController.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering.profiles +package `ordering-service`.profiles import com.coded.spring.ordering.TalabatRepository import org.springframework.web.bind.annotation.* diff --git a/src/main/kotlin/com/coded/spring/ordering/profiles/profileEntity.kt b/ordercenter/src/main/kotlin/ordering-service/profiles/profileEntity.kt similarity index 90% rename from src/main/kotlin/com/coded/spring/ordering/profiles/profileEntity.kt rename to ordercenter/src/main/kotlin/ordering-service/profiles/profileEntity.kt index 6ec4759..278fcb3 100644 --- a/src/main/kotlin/com/coded/spring/ordering/profiles/profileEntity.kt +++ b/ordercenter/src/main/kotlin/ordering-service/profiles/profileEntity.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering.profiles +package `ordering-service`.profiles import com.coded.spring.ordering.UserEntity import jakarta.persistence.* diff --git a/src/main/kotlin/com/coded/spring/ordering/profiles/profileRepository.kt b/ordercenter/src/main/kotlin/ordering-service/profiles/profileRepository.kt similarity index 65% rename from src/main/kotlin/com/coded/spring/ordering/profiles/profileRepository.kt rename to ordercenter/src/main/kotlin/ordering-service/profiles/profileRepository.kt index 97a7cdb..4ac31ed 100644 --- a/src/main/kotlin/com/coded/spring/ordering/profiles/profileRepository.kt +++ b/ordercenter/src/main/kotlin/ordering-service/profiles/profileRepository.kt @@ -1,6 +1,7 @@ -package com.coded.spring.ordering.profiles +package `ordering-service`.profiles import jakarta.inject.Named +import `ordering-service`.profiles.ProfileEntity import org.springframework.data.jpa.repository.JpaRepository @Named diff --git a/pom.xml b/pom.xml index f59d09b..1b0088f 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ com.coded.spring Ordering 0.0.1-SNAPSHOT + pom Kotlin.SpringbootV2 Kotlin.SpringbootV2 @@ -20,6 +21,11 @@ + + auth + ordercenter + welcoming + diff --git a/src/main/kotlin/com/coded/spring/ordering/menu/menuCache.kt b/src/main/kotlin/com/coded/spring/ordering/menu/menuCache.kt deleted file mode 100644 index c62bb76..0000000 --- a/src/main/kotlin/com/coded/spring/ordering/menu/menuCache.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.coded.spring.ordering.menu - -import com.coded.spring.ordering.cache.menuHazelcastInstance - -val menuCache = menuHazelcastInstance.getMap>("menu") diff --git a/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt b/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt index 45a40f8..81fba0d 100644 --- a/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt +++ b/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt @@ -1,106 +1,106 @@ -package com.coded.spring.ordering - -import com.coded.spring.ordering.orders.OrderRepository -import com.coded.spring.ordering.profiles.ProfileEntity -import com.coded.spring.ordering.profiles.ProfileRepository -import com.coded.spring.ordering.profiles.ProfileRequest -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.web.client.TestRestTemplate -import org.springframework.boot.test.web.server.LocalServerPort -import org.springframework.http.HttpEntity -import org.springframework.http.HttpHeaders -import org.springframework.http.HttpStatus -import org.springframework.http.MediaType -import org.springframework.security.crypto.password.PasswordEncoder -import org.springframework.test.context.ActiveProfiles - -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@ActiveProfiles("test") -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class ProfileControllerTest { - - @LocalServerPort - var port: Int = 0 - - @Autowired - lateinit var usersRepository: TalabatRepository - - @Autowired - lateinit var ordersRepository: OrderRepository - - @Autowired - lateinit var profileRepository: ProfileRepository - - @Autowired - lateinit var passwordEncoder: PasswordEncoder - - @Autowired - lateinit var restTemplate: TestRestTemplate - - lateinit var tokenUsername: String - lateinit var tokenPassword: String - - lateinit var token: String - - @BeforeAll - fun setUp() { - val username = "ZainabAlS" - val rawPassword = "joincoded" - - if (usersRepository.findByUsername(username) == null) { - val testUser = UserEntity( - name = "Zainab", - username = username, - password = passwordEncoder.encode(rawPassword) - ) - usersRepository.save(testUser) - } - - val loginRequest = AuthenticationRequest(username, rawPassword) - val loginResponse = restTemplate.postForEntity( - "http://localhost:$port/authentication/login", - loginRequest, - AuthenticationResponse::class.java - ) - - assertEquals(HttpStatus.OK, loginResponse.statusCode) - token = loginResponse.body?.token ?: throw IllegalStateException("Token was not returned") - - tokenUsername = username - tokenPassword = rawPassword - } - - - @Test - fun `authenticated user can create profile`() { - val request = ProfileRequest( - firstName = "Zainab", - lastName = "AlSaffar", - phoneNumber = "12345678" - ) - - val headers = HttpHeaders().apply { - contentType = MediaType.APPLICATION_JSON - setBearerAuth(token) - } - - val entity = HttpEntity(request, headers) - - val response = restTemplate.postForEntity( - "http://localhost:$port/profile", - entity, - ProfileEntity::class.java - ) - - assertEquals(HttpStatus.OK, response.statusCode) - assertNotNull(response.body) - assertEquals("Zainab", response.body?.firstName) - assertEquals("AlSaffar", response.body?.lastName) - } - -} +//package com.coded.spring.ordering +// +//import com.coded.spring.ordering.orders.OrderRepository +//import com.coded.spring.ordering.profiles.ProfileEntity +//import com.coded.spring.ordering.profiles.ProfileRepository +//import com.coded.spring.ordering.profiles.ProfileRequest +//import org.junit.jupiter.api.Assertions.* +//import org.junit.jupiter.api.BeforeAll +//import org.junit.jupiter.api.Test +//import org.junit.jupiter.api.TestInstance +//import org.springframework.beans.factory.annotation.Autowired +//import org.springframework.boot.test.context.SpringBootTest +//import org.springframework.boot.test.web.client.TestRestTemplate +//import org.springframework.boot.test.web.server.LocalServerPort +//import org.springframework.http.HttpEntity +//import org.springframework.http.HttpHeaders +//import org.springframework.http.HttpStatus +//import org.springframework.http.MediaType +//import org.springframework.security.crypto.password.PasswordEncoder +//import org.springframework.test.context.ActiveProfiles +// +//@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +//@ActiveProfiles("test") +//@TestInstance(TestInstance.Lifecycle.PER_CLASS) +//class ProfileControllerTest { +// +// @LocalServerPort +// var port: Int = 0 +// +// @Autowired +// lateinit var usersRepository: TalabatRepository +// +// @Autowired +// lateinit var ordersRepository: OrderRepository +// +// @Autowired +// lateinit var profileRepository: ProfileRepository +// +// @Autowired +// lateinit var passwordEncoder: PasswordEncoder +// +// @Autowired +// lateinit var restTemplate: TestRestTemplate +// +// lateinit var tokenUsername: String +// lateinit var tokenPassword: String +// +// lateinit var token: String +// +// @BeforeAll +// fun setUp() { +// val username = "ZainabAlS" +// val rawPassword = "joincoded" +// +// if (usersRepository.findByUsername(username) == null) { +// val testUser = UserEntity( +// name = "Zainab", +// username = username, +// password = passwordEncoder.encode(rawPassword) +// ) +// usersRepository.save(testUser) +// } +// +// val loginRequest = AuthenticationRequest(username, rawPassword) +// val loginResponse = restTemplate.postForEntity( +// "http://localhost:$port/authentication/login", +// loginRequest, +// AuthenticationResponse::class.java +// ) +// +// assertEquals(HttpStatus.OK, loginResponse.statusCode) +// token = loginResponse.body?.token ?: throw IllegalStateException("Token was not returned") +// +// tokenUsername = username +// tokenPassword = rawPassword +// } +// +// +// @Test +// fun `authenticated user can create profile`() { +// val request = ProfileRequest( +// firstName = "Zainab", +// lastName = "AlSaffar", +// phoneNumber = "12345678" +// ) +// +// val headers = HttpHeaders().apply { +// contentType = MediaType.APPLICATION_JSON +// setBearerAuth(token) +// } +// +// val entity = HttpEntity(request, headers) +// +// val response = restTemplate.postForEntity( +// "http://localhost:$port/profile", +// entity, +// ProfileEntity::class.java +// ) +// +// assertEquals(HttpStatus.OK, response.statusCode) +// assertNotNull(response.body) +// assertEquals("Zainab", response.body?.firstName) +// assertEquals("AlSaffar", response.body?.lastName) +// } +// +//} diff --git a/welcoming/pom.xml b/welcoming/pom.xml new file mode 100644 index 0000000..e017ff7 --- /dev/null +++ b/welcoming/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + + com.coded.spring + Ordering + 0.0.1-SNAPSHOT + + + welcoming + + + UTF-8 + official + 1.8 + + + + + mavenCentral + https://repo1.maven.org/maven2/ + + + + + src/main/kotlin + src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + maven-surefire-plugin + 2.22.2 + + + maven-failsafe-plugin + 2.22.2 + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + MainKt + + + + + + + + org.jetbrains.kotlin + kotlin-test-junit5 + 2.1.0 + test + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + org.jetbrains.kotlin + kotlin-stdlib + 2.1.0 + + + com.coded.spring + auth + 0.0.1-SNAPSHOT + + + + + \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/Application.kt b/welcoming/src/main/kotlin/welcome-service/Application.kt similarity index 89% rename from src/main/kotlin/com/coded/spring/ordering/Application.kt rename to welcoming/src/main/kotlin/welcome-service/Application.kt index 590d1ce..8da59dc 100644 --- a/src/main/kotlin/com/coded/spring/ordering/Application.kt +++ b/welcoming/src/main/kotlin/welcome-service/Application.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering +package com.coded.spring.`welcome-service` import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.context.properties.EnableConfigurationProperties diff --git a/src/main/kotlin/com/coded/spring/ordering/appConfiguration.kt b/welcoming/src/main/kotlin/welcome-service/appConfiguration.kt similarity index 90% rename from src/main/kotlin/com/coded/spring/ordering/appConfiguration.kt rename to welcoming/src/main/kotlin/welcome-service/appConfiguration.kt index d17ab95..afa2f64 100644 --- a/src/main/kotlin/com/coded/spring/ordering/appConfiguration.kt +++ b/welcoming/src/main/kotlin/welcome-service/appConfiguration.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering +package com.coded.spring.`welcome-service` import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.context.annotation.Configuration diff --git a/src/main/resources/application.properties b/welcoming/src/main/kotlin/welcome-service/resources/application.properties similarity index 100% rename from src/main/resources/application.properties rename to welcoming/src/main/kotlin/welcome-service/resources/application.properties diff --git a/welcoming/src/main/kotlin/welcome-service/welcomeController.kt b/welcoming/src/main/kotlin/welcome-service/welcomeController.kt new file mode 100644 index 0000000..846a65e --- /dev/null +++ b/welcoming/src/main/kotlin/welcome-service/welcomeController.kt @@ -0,0 +1,21 @@ +package com.coded.spring.`welcome-service` + +import com.coded.spring.`welcome-service`.AppProperties +import org.springframework.web.bind.annotation.* +import org.springframework.security.crypto.password.PasswordEncoder + +@RestController +class TalabatController( + private val talabatRepository: TalabatRepository, + private val passwordEncoder: PasswordEncoder, + private val appProperties: AppProperties +) { + + @GetMapping("/welcome") + fun welcomeToTalabat(): String { + return if (appProperties.festive.enabled) { + appProperties.festive.message + } else { + "Welcome to Online Ordering by ${appProperties.companyName}" + } + } \ No newline at end of file diff --git a/zainab-alsaffar-online-ordering-api-swagger-01.json b/zainab-alsaffar-online-ordering-api-swagger-01.json new file mode 100644 index 0000000..73ab2c9 --- /dev/null +++ b/zainab-alsaffar-online-ordering-api-swagger-01.json @@ -0,0 +1 @@ +{"openapi":"3.0.1","info":{"title":"OpenAPI definition","version":"v0"},"servers":[{"url":"http://localhost:8080","description":"Generated server url"}],"paths":{"/profile":{"post":{"tags":["profile-controller"],"operationId":"createProfile","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ProfileEntity"}}}}}}},"/orders":{"post":{"tags":["orders-controller"],"operationId":"createOrder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OrderEntity"}}}}}}},"/order":{"post":{"tags":["talabat-controller"],"operationId":"submitOrder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"string"}}}}}}},"/menu":{"get":{"tags":["menu-controller"],"operationId":"getMenu","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MenuEntity"}}}}}}},"post":{"tags":["menu-controller"],"operationId":"addMenuItem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MenuRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MenuEntity"}}}}}}},"/clients":{"post":{"tags":["talabat-controller"],"operationId":"clients","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientsRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserEntity"}}}}}}},"/authentication/login":{"post":{"tags":["authentication-controller"],"operationId":"login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AuthenticationResponse"}}}}}}},"/welcome":{"get":{"tags":["talabat-controller"],"operationId":"welcomeToTalabat","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"string"}}}}}}},"/orders/v1/list":{"get":{"tags":["orders-controller"],"operationId":"listOrders","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OrderEntity"}}}}}}}}},"components":{"schemas":{"ProfileRequest":{"required":["firstName","lastName","phoneNumber"],"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"phoneNumber":{"type":"string"}}},"ProfileEntity":{"required":["firstName","lastName","phoneNumber"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"user":{"$ref":"#/components/schemas/UserEntity"},"firstName":{"type":"string"},"lastName":{"type":"string"},"phoneNumber":{"type":"string"}}},"UserEntity":{"required":["name","password","username"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"username":{"type":"string"},"password":{"type":"string"}}},"CreateOrderRequest":{"required":["items","restaurant","userId"],"type":"object","properties":{"userId":{"type":"integer","format":"int64"},"restaurant":{"type":"string"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ItemRequest"}}}},"ItemRequest":{"required":["name","quantity"],"type":"object","properties":{"name":{"type":"string"},"quantity":{"type":"integer","format":"int32"}}},"OrderEntity":{"required":["items","restaurant","user"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"user":{"$ref":"#/components/schemas/UserEntity"},"restaurant":{"type":"string"},"items":{"type":"string"}}},"OrderRequest":{"required":["items"],"type":"object","properties":{"items":{"type":"array","items":{"type":"string"}}}},"MenuRequest":{"required":["name","price"],"type":"object","properties":{"name":{"type":"string"},"price":{"type":"number","format":"double"}}},"MenuEntity":{"required":["name","price"],"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"price":{"type":"number","format":"double"}}},"ClientsRequest":{"required":["name","password","username"],"type":"object","properties":{"name":{"type":"string"},"username":{"type":"string"},"password":{"type":"string"}}},"AuthenticationRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"AuthenticationResponse":{"required":["token"],"type":"object","properties":{"token":{"type":"string"}}}}}} \ No newline at end of file