diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 0e1e30b892c76f654a2596fb8415f891f05eedfb..398f1633077b69fdad5eda27883d2524ea31c141 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -12,6 +12,7 @@ services: container_name: 'test_corporate_builder2' depends_on: - test_db + - test_mbroker working_dir: '/app' networks: - test_corporate_network @@ -31,4 +32,12 @@ services: networks: test_corporate_network: aliases: - - corporate_db2 \ No newline at end of file + - corporate_db2 + + test_mbroker: + image: 'rmohr/activemq:5.15.9' + container_name: 'test_corporate_broker2' + networks: + test_corporate_network: + aliases: + - actvemq \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 09eba51bf484cfc2417414989601d199d170c9c5..c3b27178a1c4a2380333d6f8ff7320a6d953da01 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,7 @@ version: '3.7' - +networks: + corporate_app2_prod_net: + name: 'ca2pd' services: boot: @@ -12,6 +14,8 @@ services: depends_on: - db working_dir: '/app' + networks: + - corporate_app2_prod_net volumes: - .:/app - m2:/root/.m2 @@ -31,6 +35,8 @@ services: image: 'postgres:10-alpine' container_name: 'corporate_db2' restart: always + networks: + - corporate_app2_prod_net environment: POSTGRES_DB: cinema POSTGRES_USER: postgres @@ -40,6 +46,21 @@ services: volumes: - db_data:/var/lib/postgresql/data + mbroker: + image: 'rmohr/activemq:5.15.9' + container_name: 'corporate_broker2' + networks: + corporate_app2_prod_net: + aliases: + - actvemq + volumes: + - active_mq_data:/opt/activemq/data + - active_mq_conf:/opt/activemq/conf + ports: + - 8161:8161 + volumes: m2: - db_data: \ No newline at end of file + db_data: + active_mq_data: + active_mq_conf: \ No newline at end of file diff --git a/pom.xml b/pom.xml index f1776a2e5ed5f87e3422e82229f46dc7b7425790..889e40ea4b08d480b43a9cc14bd4037184c0e272 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,22 @@ <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-jms</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-mail</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-activemq</artifactId> + </dependency> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>activemq-broker</artifactId> + </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> diff --git a/src/main/kotlin/com/s3ai/corporate_app2/CorporateApp2Application.kt b/src/main/kotlin/com/s3ai/corporate_app2/CorporateApp2Application.kt index feef50de9919ca0ca70044ac405dc7c9a1bc1589..151fdcb95dfbfbd0da685dd500e733cc86de2088 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/CorporateApp2Application.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/CorporateApp2Application.kt @@ -2,8 +2,11 @@ package com.s3ai.corporate_app2 import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication +import org.springframework.jms.annotation.EnableJms + @SpringBootApplication +@EnableJms class CorporateApp2Application fun main(args: Array<String>) { diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/CinemasController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/CinemasController.kt index 132b58c030359dd4aaff4e842c6cefcf87e02b60..0d17a4b4fcb76b8f76335734efdb00b570e3db8e 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/CinemasController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/CinemasController.kt @@ -2,6 +2,7 @@ package com.s3ai.corporate_app2.controllers import com.s3ai.corporate_app2.Cinema import com.s3ai.corporate_app2.CinemaService +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.HttpStatus import org.springframework.stereotype.Controller @@ -13,7 +14,6 @@ import org.springframework.web.servlet.view.RedirectView import java.lang.IllegalArgumentException import java.util.* import java.util.UUID.fromString -import java.util.UUID.randomUUID @Controller @RequestMapping("/cinemas") @@ -21,9 +21,13 @@ class CinemasController { @Autowired lateinit var cinemaService: CinemaService + @Autowired + lateinit var publisher: JMSPublisher + @GetMapping("/list") fun getCinemasBrowsePage(model: Model): String? { - model.addAttribute("cinemas", cinemaService.findAll()) + val items = getAllItems(cinemaService, publisher) + model.addAttribute("cinemas", items) return "cinemas/list" } @@ -32,7 +36,6 @@ class CinemasController { val cinema: Cinema? if (id.isEmpty()) { cinema = Cinema() - cinema.id = randomUUID() model.addAttribute("action", "Create") } else { val idParsed: UUID @@ -51,7 +54,7 @@ class CinemasController { @PostMapping("/update") fun updateCinema(@ModelAttribute cinema: Cinema): RedirectView { - cinemaService.save(cinema) + updateItem(cinemaService, cinema, cinema.id, publisher) return RedirectView("/cinemas/list") } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/SubscribersController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/SubscribersController.kt new file mode 100644 index 0000000000000000000000000000000000000000..6974267b9402aeb68a6dac26ea51a983861091ad --- /dev/null +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/SubscribersController.kt @@ -0,0 +1,63 @@ +package com.s3ai.corporate_app2.controllers + +import com.s3ai.corporate_app2.jms.persistance.* +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Controller +import org.springframework.ui.Model +import org.springframework.web.bind.annotation.* +import org.springframework.web.servlet.view.RedirectView +import java.util.* + +class FilterForm { + var actions: MutableList<JMSEntityAction> = mutableListOf() +} + +@Controller +@RequestMapping("/subscription") +class SubscribersController { + + @Autowired + lateinit var subscriberRepository: SubscribersRepository + + @Autowired + lateinit var subTypeRepo: SubscriptionTypeRepository + + @GetMapping("/subscribe") + fun getSubscribePage(model: Model): String? { + val subscriber: Subscriber? + subscriber = Subscriber() + subscriber.id = UUID.randomUUID() + model.addAttribute("subscriber", subscriber) + model.addAttribute("types", JMSEntityAction.values()) + return "subscription/subscribe" + } + + @GetMapping("/unsubscribe") + fun getUnsubscribePage(model: Model): String { + return "subscription/unsubscribe" + } + + + @PostMapping("/subscribe") + fun addSubscriber(@ModelAttribute sub: Subscriber, @ModelAttribute(name = "actions") actionsForm: FilterForm, model: Model): RedirectView { + if (actionsForm.actions.size < 1) return RedirectView("/") + val subscriber = subscriberRepository.save(sub) + for (action in actionsForm.actions) { + val newType = SubscriptionType() + newType.type = action + newType.subscriber = subscriber + subTypeRepo.save(newType) + } + return RedirectView("/") + } + + @PostMapping("/unsubscribe") + fun unsubscribeByEmail(@RequestParam(name = "email", required = true) email: String, model: Model): RedirectView { + val user = subscriberRepository.findSubscriberByEmail(email) + if (user.isPresent) { + subscriberRepository.delete(user.get()) + } + return RedirectView("/") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/TicketsController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/TicketsController.kt index 4c865d044459e4e58c23d61e66ef90d8e6733ca3..878cce6b4030d8f9a4ee6b1c50662c88ae215695 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/TicketsController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/TicketsController.kt @@ -4,6 +4,7 @@ import com.s3ai.corporate_app2.CinemaService import com.s3ai.corporate_app2.Ticket import com.s3ai.corporate_app2.TicketService import com.s3ai.corporate_app2.UserService +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.HttpStatus import org.springframework.stereotype.Controller @@ -23,10 +24,13 @@ class TicketsController { lateinit var userService: UserService @Autowired lateinit var cinemaService: CinemaService + @Autowired + lateinit var publisher: JMSPublisher @GetMapping("/list") fun getTicketsBrowsePage(model: Model): String? { - model.addAttribute("tickets", ticketService.findAll()) + val items = getAllItems(ticketService, publisher) + model.addAttribute("tickets", items) return "tickets/list" } @@ -35,7 +39,6 @@ class TicketsController { val ticket: Ticket? if (id.isEmpty()) { ticket = Ticket() - ticket.id = UUID.randomUUID() model.addAttribute("action", "Create") } else { val idParsed: UUID @@ -56,7 +59,7 @@ class TicketsController { @PostMapping("/update") fun updateTicket(@ModelAttribute ticket: Ticket): RedirectView { - ticketService.save(ticket) + updateItem(ticketService, ticket, ticket.id, publisher) return RedirectView("/tickets/list") } diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/UsersController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/UsersController.kt index 786a29335b2244650697b1ceae315df49d05959b..5ea71ad523abf891aaf7d031fd42f35c0c481130 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/UsersController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/UsersController.kt @@ -2,6 +2,7 @@ package com.s3ai.corporate_app2.controllers import com.s3ai.corporate_app2.User import com.s3ai.corporate_app2.UserService +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.HttpStatus import org.springframework.stereotype.Controller @@ -18,9 +19,13 @@ class UsersController { @Autowired lateinit var userService: UserService + @Autowired + lateinit var publisher: JMSPublisher + @GetMapping("/list") fun getUsersBrowsePage(model: Model): String? { - model.addAttribute("users", userService.findAll()) + val items = getAllItems(userService, publisher) + model.addAttribute("users", items) return "users/list" } @@ -29,7 +34,6 @@ class UsersController { val user: User? if (id.isEmpty()) { user = User() - user.id = UUID.randomUUID() model.addAttribute("action", "Create") } else { val idParsed: UUID @@ -48,7 +52,7 @@ class UsersController { @PostMapping("/update") fun updateUser(@ModelAttribute user: User): RedirectView { - userService.save(user) + updateItem(userService, user, user.id, publisher) return RedirectView("/users/list") } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/controllerUtils.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/controllerUtils.kt index 1ae2e7b11f4484631bbdb02307aaec482838d2f5..f08a73a77c534d19c6978ea5b778ddda4b1c06aa 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/controllerUtils.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/controllerUtils.kt @@ -2,6 +2,9 @@ package com.s3ai.corporate_app2.controllers import com.fasterxml.jackson.dataformat.xml.XmlMapper import com.s3ai.corporate_app2.CinemaServices +import com.s3ai.corporate_app2.jms.JMSPublisher +import com.s3ai.corporate_app2.jms.persistance.JMSAction +import com.s3ai.corporate_app2.jms.persistance.JMSEntityAction import org.springframework.util.ResourceUtils import java.io.StringReader import java.io.StringWriter @@ -11,16 +14,25 @@ import javax.xml.transform.TransformerFactory import javax.xml.transform.stream.StreamResult import javax.xml.transform.stream.StreamSource +fun publishMessage(publisher: JMSPublisher, entity: String, actionJMS: JMSEntityAction, description: String) { + val jmsAction = JMSAction() + jmsAction.entity = entity + jmsAction.actionJMS = actionJMS + jmsAction.actionDescription = description + publisher.publish(jmsAction) +} -fun <T> deleteInstance(id: String, instanceName: String, jpaService: CinemaServices<T>, response: HttpServletResponse): String? { +fun <T> deleteInstance(id: String, jpaService: CinemaServices<T>, response: HttpServletResponse, publisher: JMSPublisher): String? { var responseString: String? = null try { val itemId = UUID.fromString(id) - val user = jpaService.findById(itemId) - if (null != user) jpaService.delete(user) - else { + val item = jpaService.findById(itemId) + if (null != item) { + jpaService.delete(item) + publishMessage(publisher, jpaService.itemName(), JMSEntityAction.DELETE, "Deleted object: $item") + } else { response.status = HttpServletResponse.SC_NOT_FOUND - responseString = "$instanceName was not Found" + responseString = "${jpaService.itemName()} was not Found" } } catch (e: IllegalArgumentException) { response.status = HttpServletResponse.SC_BAD_REQUEST @@ -29,14 +41,30 @@ fun <T> deleteInstance(id: String, instanceName: String, jpaService: CinemaServi return responseString } -fun <T> getInstance(id: UUID, jpaService: CinemaServices<T>, response: HttpServletResponse): T? { +fun <T : Any> getInstance(id: UUID, jpaService: CinemaServices<T>, response: HttpServletResponse, publisher: JMSPublisher): T? { val item = jpaService.findById(id) - if (null != item) return item - else response.status = HttpServletResponse.SC_NOT_FOUND + publishMessage(publisher, jpaService.itemName(), JMSEntityAction.QUERY, "Queried object with id: $id, Found: $item") + if (null != item) { + return item + } else response.status = HttpServletResponse.SC_NOT_FOUND return null } -fun XMLTransform(item: Any, resource_name: String): String { +fun <T : Any> getAllItems(jpaService: CinemaServices<T>, publisher: JMSPublisher): MutableList<T> { + val items = jpaService.findAll() + publishMessage(publisher, jpaService.itemName(), JMSEntityAction.QUERY, "Queried all items, found ${items.size}.") + return items +} + +fun <T : Any> updateItem(jpaService: CinemaServices<T>, item: T, item_id: UUID?, publisher: JMSPublisher) { + var action = JMSEntityAction.UPDATE + val updatedItem = jpaService.save(item) + val description = "New item data: $updatedItem" + if (null == item_id) action = JMSEntityAction.CREATE + publishMessage(publisher, jpaService.itemName(), action, description) +} + +fun xsltTransform(item: Any, resource_name: String): String { val xmlMapper = XmlMapper() val xslt = StreamSource(ResourceUtils.getFile("classpath:xslt/$resource_name")) val xml = StreamSource(StringReader(xmlMapper.writeValueAsString(item))) @@ -44,13 +72,4 @@ fun XMLTransform(item: Any, resource_name: String): String { val sw = StringWriter() transformer.transform(xml, StreamResult(sw)) return sw.toString() -} - -fun <T> getXSLTransformedInstance(id: UUID, jpaService: CinemaServices<T>, resource_name: String, response: HttpServletResponse): String { - val item = jpaService.findById(id) - if (null == item) response.status = HttpServletResponse.SC_NOT_FOUND - else { - return XMLTransform(item, resource_name) - } - return "" -} +} \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/CinemaApiController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/CinemaApiController.kt index b734aa12a01e7d566a5d02fcbb8693177abef7b1..a679374c9834c6eacf36c445c2b71c105e49b619 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/CinemaApiController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/CinemaApiController.kt @@ -3,6 +3,7 @@ package com.s3ai.corporate_app2.controllers.rest import com.s3ai.corporate_app2.Cinema import com.s3ai.corporate_app2.CinemaService import com.s3ai.corporate_app2.controllers.deleteInstance +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.web.bind.annotation.* import java.util.* @@ -15,6 +16,9 @@ class CinemaApiController { @Autowired lateinit var cinemaService: CinemaService + @Autowired + lateinit var publisher: JMSPublisher + @GetMapping("/fill") fun fillCinemas(): String? { val locations: List<String> = listOf("Samara", "Izhevsk", "NeoTokyo", "Bangladesh", "Moscow", "Grozny"); @@ -34,7 +38,7 @@ class CinemaApiController { @DeleteMapping("/delete") fun deleteCinema(@RequestParam(name = "id", required = true) id: String, response: HttpServletResponse): String? { - return deleteInstance(id, "Cinema", cinemaService, response) + return deleteInstance(id, cinemaService, response, publisher) } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/TicketApiController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/TicketApiController.kt index d7b371151653b0989cb5c912c2e866a67b9d37a4..6de73ce6ee8c794673faa0be57b245872c870425 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/TicketApiController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/TicketApiController.kt @@ -5,6 +5,7 @@ import com.s3ai.corporate_app2.Ticket import com.s3ai.corporate_app2.TicketService import com.s3ai.corporate_app2.UserService import com.s3ai.corporate_app2.controllers.deleteInstance +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.web.bind.annotation.* import java.util.* @@ -15,11 +16,16 @@ import javax.servlet.http.HttpServletResponse class TicketApiController { @Autowired lateinit var userService: UserService + @Autowired lateinit var ticketService: TicketService + @Autowired lateinit var cinemaService: CinemaService + @Autowired + lateinit var publisher: JMSPublisher + @GetMapping("/fill") fun fillTickets(): String? { val wordsFirst = arrayOf("Pirates", "Ladies", "Goblins", "Gangsters", "Programmers", "Hackers", "Wrestlers", "Animals", "Guardians") @@ -44,7 +50,7 @@ class TicketApiController { @DeleteMapping("/delete") fun deleteTicket(@RequestParam(name = "id", required = true) id: String, response: HttpServletResponse): String? { - return deleteInstance(id, "Ticket", ticketService, response) + return deleteInstance(id, ticketService, response, publisher) } } diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/UserApiController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/UserApiController.kt index b9a0a8ca1612c25436ca6f78f8441506a585c79e..193396cb30d3d7f6645b849bbe15b576ec3ee871 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/UserApiController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/UserApiController.kt @@ -3,6 +3,7 @@ package com.s3ai.corporate_app2.controllers.rest; import com.s3ai.corporate_app2.User import com.s3ai.corporate_app2.UserService import com.s3ai.corporate_app2.controllers.deleteInstance +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.web.bind.annotation.* import java.util.* @@ -12,9 +13,13 @@ import kotlin.random.Random @RestController @RequestMapping("/api/users") class UserApiController { + @Autowired lateinit var userService: UserService + @Autowired + lateinit var publisher: JMSPublisher + @GetMapping("/fill") fun fillCinemas(): String? { val names = arrayOf("Jane", "Mary", "Paul", "Jason", "Keanu", "Andrew", "Joseph", "Jotaro", "Ivan", "Jolyne", "Walther"); @@ -25,7 +30,8 @@ class UserApiController { user.id = UUID.randomUUID(); user.name = "${names.random()} ${surnames.random()}"; generatedNames.add(user.name.toString()) - user.age = Random.nextInt(15, 70); + user.age = Random.nextInt(15, 70) + userService.save(user); } return generatedNames.toString() @@ -33,6 +39,6 @@ class UserApiController { @DeleteMapping("/delete") fun deleteUser(@RequestParam(name = "id", required = true) id: String, response: HttpServletResponse): String? { - return deleteInstance(id, "User", userService, response) + return deleteInstance(id, userService, response, publisher) } } diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/CinemasJsonController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/CinemasJsonController.kt index 033ea39a4dc654989dd0656349f037526162a3c1..6cd1a4959c9e088f9fe85495ea532e83d70a219d 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/CinemasJsonController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/CinemasJsonController.kt @@ -2,7 +2,9 @@ package com.s3ai.corporate_app2.controllers.rest.json import com.s3ai.corporate_app2.Cinema import com.s3ai.corporate_app2.CinemaService +import com.s3ai.corporate_app2.controllers.getAllItems import com.s3ai.corporate_app2.controllers.getInstance +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping @@ -18,14 +20,17 @@ class CinemasJsonController { @Autowired lateinit var cinemaService: CinemaService + @Autowired + lateinit var publisher: JMSPublisher + @GetMapping("/all", produces = [MediaType.APPLICATION_JSON_VALUE]) fun getCinemas(response: HttpServletResponse): MutableList<Cinema> { - return cinemaService.findAll() + return getAllItems(cinemaService, publisher) } @GetMapping("/item/{id}", produces = [MediaType.APPLICATION_JSON_VALUE]) fun getCinema(@PathVariable id: UUID, response: HttpServletResponse): Cinema? { - return getInstance(id, cinemaService, response) + return getInstance(id, cinemaService, response, publisher) } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/TicketsJsonController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/TicketsJsonController.kt index 12e7eec1804795371bfe8cd0a22c2b943da871c9..95474bdcaeb0976c7ace981adc14ffcd2c5d34e5 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/TicketsJsonController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/TicketsJsonController.kt @@ -2,7 +2,9 @@ package com.s3ai.corporate_app2.controllers.rest.json import com.s3ai.corporate_app2.Ticket import com.s3ai.corporate_app2.TicketService +import com.s3ai.corporate_app2.controllers.getAllItems import com.s3ai.corporate_app2.controllers.getInstance +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping @@ -18,14 +20,17 @@ class TicketsJsonController { @Autowired lateinit var ticketService: TicketService + @Autowired + lateinit var publisher: JMSPublisher + @GetMapping("/all", produces = [MediaType.APPLICATION_JSON_VALUE]) fun getTickets(response: HttpServletResponse): MutableList<Ticket> { - return ticketService.findAll() + return getAllItems(ticketService, publisher) } @GetMapping("/item/{id}", produces = [MediaType.APPLICATION_JSON_VALUE]) fun getTicket(@PathVariable id: UUID, response: HttpServletResponse): Ticket? { - return getInstance(id, ticketService, response) + return getInstance(id, ticketService, response, publisher) } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/UsersJsonController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/UsersJsonController.kt index 72cb67d4e148604740d5f3d14a4526ca545bf35f..a6d10f556184aefd3a21acd28b65babd746fad1b 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/UsersJsonController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/json/UsersJsonController.kt @@ -2,7 +2,9 @@ package com.s3ai.corporate_app2.controllers.rest.json import com.s3ai.corporate_app2.User import com.s3ai.corporate_app2.UserService +import com.s3ai.corporate_app2.controllers.getAllItems import com.s3ai.corporate_app2.controllers.getInstance +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping @@ -18,14 +20,17 @@ class UsersJsonController { @Autowired lateinit var userService: UserService + @Autowired + lateinit var publisher: JMSPublisher + @GetMapping("/all", produces = [MediaType.APPLICATION_JSON_VALUE]) fun getUsers(response: HttpServletResponse): MutableList<User> { - return userService.findAll() + return getAllItems(userService, publisher) } @GetMapping("/item/{id}", produces = [MediaType.APPLICATION_JSON_VALUE]) fun getUser(@PathVariable id: UUID, response: HttpServletResponse): User? { - return getInstance(id, userService, response) + return getInstance(id, userService, response, publisher) } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/CinemasXmlController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/CinemasXmlController.kt index c5664290c659e70456f6097df0a31c91e7bef535..295c82cc3a6d1ce712bd1a0634a0e0db42c4c5b9 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/CinemasXmlController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/CinemasXmlController.kt @@ -2,7 +2,9 @@ package com.s3ai.corporate_app2.controllers.rest.xml import com.s3ai.corporate_app2.Cinema import com.s3ai.corporate_app2.CinemaService +import com.s3ai.corporate_app2.controllers.getAllItems import com.s3ai.corporate_app2.controllers.getInstance +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping @@ -18,14 +20,17 @@ class CinemasXmlController { @Autowired lateinit var cinemaService: CinemaService + @Autowired + lateinit var publisher: JMSPublisher + @GetMapping("/all", produces = [MediaType.APPLICATION_XML_VALUE]) fun getCinemas(response: HttpServletResponse): MutableList<Cinema> { - return cinemaService.findAll() + return getAllItems(cinemaService, publisher) } @GetMapping("/item/{id}", produces = [MediaType.APPLICATION_XML_VALUE]) fun getCinema(@PathVariable id: UUID, response: HttpServletResponse): Cinema? { - return getInstance(id, cinemaService, response) + return getInstance(id, cinemaService, response, publisher) } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/TicketsXmlController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/TicketsXmlController.kt index ddd5b7910103e9e6c005a47f3d4551ea08fd8c7f..4648abedae86ecea36b5d1d34a4b107c78516afb 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/TicketsXmlController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/TicketsXmlController.kt @@ -2,7 +2,9 @@ package com.s3ai.corporate_app2.controllers.rest.xml import com.s3ai.corporate_app2.Ticket import com.s3ai.corporate_app2.TicketService +import com.s3ai.corporate_app2.controllers.getAllItems import com.s3ai.corporate_app2.controllers.getInstance +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping @@ -18,13 +20,16 @@ class TicketsXmlController { @Autowired lateinit var ticketService: TicketService + @Autowired + lateinit var publisher: JMSPublisher + @GetMapping("/all", produces = [MediaType.APPLICATION_XML_VALUE]) fun getTickets(response: HttpServletResponse): MutableList<Ticket> { - return ticketService.findAll() + return getAllItems(ticketService, publisher) } @GetMapping("/item/{id}", produces = [MediaType.APPLICATION_XML_VALUE]) fun getTicket(@PathVariable id: UUID, response: HttpServletResponse): Ticket? { - return getInstance(id, ticketService, response) + return getInstance(id, ticketService, response, publisher) } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/UsersXmlController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/UsersXmlController.kt index 4de521bf1ec6d54200eb2d943ab6d8b1082d3bc9..cb633d5bad351b0bb80098e838b866d942f6ed5f 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/UsersXmlController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xml/UsersXmlController.kt @@ -2,7 +2,9 @@ package com.s3ai.corporate_app2.controllers.rest.xml import com.s3ai.corporate_app2.User import com.s3ai.corporate_app2.UserService +import com.s3ai.corporate_app2.controllers.getAllItems import com.s3ai.corporate_app2.controllers.getInstance +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping @@ -18,13 +20,16 @@ class UsersXmlController { @Autowired lateinit var userService: UserService + @Autowired + lateinit var publisher: JMSPublisher + @GetMapping("/all", produces = [MediaType.APPLICATION_XML_VALUE]) fun getUsers(response: HttpServletResponse): MutableList<User> { - return userService.findAll() + return getAllItems(userService, publisher) } @GetMapping("/item/{id}", produces = [MediaType.APPLICATION_XML_VALUE]) fun getUser(@PathVariable id: UUID, response: HttpServletResponse): User? { - return getInstance(id, userService, response) + return getInstance(id, userService, response, publisher) } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/CinemasXsltController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/CinemasXsltController.kt index 498cd0a4c3c3e7330ee2e3eb1e19bbbf61a68898..c7253791b17443e9dd538894f18ab78b12890eb3 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/CinemasXsltController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/CinemasXsltController.kt @@ -1,8 +1,10 @@ package com.s3ai.corporate_app2.controllers.rest.xslt import com.s3ai.corporate_app2.CinemaService -import com.s3ai.corporate_app2.controllers.XMLTransform -import com.s3ai.corporate_app2.controllers.getXSLTransformedInstance +import com.s3ai.corporate_app2.controllers.xsltTransform +import com.s3ai.corporate_app2.controllers.getAllItems +import com.s3ai.corporate_app2.controllers.getInstance +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping @@ -18,16 +20,23 @@ class CinemasXsltController { @Autowired lateinit var cinemaService: CinemaService + @Autowired + lateinit var publisher: JMSPublisher + val xsltResourceName: String = "cinema-process.xslt" @GetMapping("/all", produces = [MediaType.TEXT_HTML_VALUE]) fun getCinemas(response: HttpServletResponse): String { - return XMLTransform(cinemaService.findAll(), xsltResourceName) + return xsltTransform(getAllItems(cinemaService, publisher), xsltResourceName) } @GetMapping("/item/{id}", produces = [MediaType.TEXT_HTML_VALUE]) fun getCinema(@PathVariable id: UUID, response: HttpServletResponse): String? { - return getXSLTransformedInstance(id, cinemaService, xsltResourceName, response) + val item = getInstance(id, cinemaService, response, publisher) + if (null != item) { + return xsltTransform(item, xsltResourceName) + } + return "" } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/TicketsXsltController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/TicketsXsltController.kt index 896eca4cfa2307bd44cbbd5248e43eb0414dfc77..698b878de1173e24fa10ceb196b92e18a5b0bfd7 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/TicketsXsltController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/TicketsXsltController.kt @@ -1,8 +1,10 @@ package com.s3ai.corporate_app2.controllers.rest.xslt import com.s3ai.corporate_app2.TicketService -import com.s3ai.corporate_app2.controllers.XMLTransform -import com.s3ai.corporate_app2.controllers.getXSLTransformedInstance +import com.s3ai.corporate_app2.controllers.xsltTransform +import com.s3ai.corporate_app2.controllers.getAllItems +import com.s3ai.corporate_app2.controllers.getInstance +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping @@ -18,16 +20,23 @@ class TicketsXsltController { @Autowired lateinit var ticketService: TicketService + @Autowired + lateinit var publisher: JMSPublisher + val xsltResourceName: String = "ticket-process.xslt" @GetMapping("/all", produces = [MediaType.TEXT_HTML_VALUE]) - fun getCinemas(response: HttpServletResponse): String { - return XMLTransform(ticketService.findAll(), xsltResourceName) + fun getTickets(response: HttpServletResponse): String { + return xsltTransform(getAllItems(ticketService, publisher), xsltResourceName) } @GetMapping("/item/{id}", produces = [MediaType.TEXT_HTML_VALUE]) - fun getCinema(@PathVariable id: UUID, response: HttpServletResponse): String? { - return getXSLTransformedInstance(id, ticketService, xsltResourceName, response) + fun getTicket(@PathVariable id: UUID, response: HttpServletResponse): String? { + val item = getInstance(id, ticketService, response, publisher) + if (null != item) { + return xsltTransform(item, xsltResourceName) + } + return "" } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/UsersXsltController.kt b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/UsersXsltController.kt index f07ee83001d9f34d50759b285b85bfaac6f733d5..c798bd48b177065a54169a2dd6c0102d111fc233 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/UsersXsltController.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/controllers/rest/xslt/UsersXsltController.kt @@ -1,8 +1,10 @@ package com.s3ai.corporate_app2.controllers.rest.xslt import com.s3ai.corporate_app2.UserService -import com.s3ai.corporate_app2.controllers.XMLTransform -import com.s3ai.corporate_app2.controllers.getXSLTransformedInstance +import com.s3ai.corporate_app2.controllers.getAllItems +import com.s3ai.corporate_app2.controllers.getInstance +import com.s3ai.corporate_app2.controllers.xsltTransform +import com.s3ai.corporate_app2.jms.JMSPublisher import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping @@ -18,16 +20,23 @@ class UsersXsltController { @Autowired lateinit var userService: UserService + @Autowired + lateinit var publisher: JMSPublisher + val xsltResourceName: String = "user-process.xslt" @GetMapping("/all", produces = [MediaType.TEXT_HTML_VALUE]) - fun getCinemas(response: HttpServletResponse): String { - return XMLTransform(userService.findAll(), xsltResourceName) + fun getUsers(response: HttpServletResponse): String { + return xsltTransform(getAllItems(userService, publisher), xsltResourceName) } @GetMapping("/item/{id}", produces = [MediaType.TEXT_HTML_VALUE]) - fun getCinema(@PathVariable id: UUID, response: HttpServletResponse): String? { - return getXSLTransformedInstance(id, userService, xsltResourceName, response) + fun getUser(@PathVariable id: UUID, response: HttpServletResponse): String? { + val item = getInstance(id, userService, response, publisher) + if (null != item) { + return xsltTransform(item, xsltResourceName) + } + return "" } } \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/entities.kt b/src/main/kotlin/com/s3ai/corporate_app2/entities.kt index 28ceb6de27ba2516cba897c2e0c56c4c03ca1897..e3f4d8dee784b5ea2c71a427657120d78b4c9981 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/entities.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/entities.kt @@ -1,8 +1,6 @@ package com.s3ai.corporate_app2 import com.fasterxml.jackson.annotation.JsonAutoDetect -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement import org.hibernate.annotations.OnDelete import org.hibernate.annotations.OnDeleteAction import java.util.* @@ -73,5 +71,4 @@ class Ticket { override fun toString(): String { return "Ticket{id=$id, user=$user, cinema=$cinema, movie='$movie'}" } -} - +} \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/jms/Config.kt b/src/main/kotlin/com/s3ai/corporate_app2/jms/Config.kt new file mode 100644 index 0000000000000000000000000000000000000000..55e003c54139624724f8ec4f96e279d4d46ad18e --- /dev/null +++ b/src/main/kotlin/com/s3ai/corporate_app2/jms/Config.kt @@ -0,0 +1,29 @@ +package com.s3ai.corporate_app2.jms + +import org.apache.activemq.ActiveMQConnectionFactory +import org.apache.activemq.command.ActiveMQQueue +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.jms.core.JmsTemplate +import javax.jms.Queue + +@Configuration +class Config { + @Value("\${spring.activemq.broker-url}") + lateinit var brokerURL: String + + @Bean + fun queue(): Queue = ActiveMQQueue("cinemas.queue") + + @Bean + fun connectionFactory(): ActiveMQConnectionFactory { + val factory = ActiveMQConnectionFactory() + factory.brokerURL = brokerURL + return factory + } + + @Bean + fun jmsTemplate(): JmsTemplate = JmsTemplate(connectionFactory()) + +} \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/jms/JMSPublisher.kt b/src/main/kotlin/com/s3ai/corporate_app2/jms/JMSPublisher.kt new file mode 100644 index 0000000000000000000000000000000000000000..631df1b2bc79efa432a5e63907aac8a4743aa6d8 --- /dev/null +++ b/src/main/kotlin/com/s3ai/corporate_app2/jms/JMSPublisher.kt @@ -0,0 +1,25 @@ +package com.s3ai.corporate_app2.jms + +import com.fasterxml.jackson.databind.ObjectMapper +import com.s3ai.corporate_app2.jms.persistance.JMSAction +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.jms.core.JmsTemplate +import org.springframework.stereotype.Component +import javax.jms.Queue + +@Component +class JMSPublisher { + @Autowired + private lateinit var jmsTemplate: JmsTemplate + + + @Autowired + private lateinit var queue: Queue + + private final val mapper: ObjectMapper = ObjectMapper() + + fun publish(message_obj: JMSAction) { + val messageText = mapper.writeValueAsString(message_obj) + jmsTemplate.convertAndSend(queue, messageText) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/jms/JmsConsumer.kt b/src/main/kotlin/com/s3ai/corporate_app2/jms/JmsConsumer.kt new file mode 100644 index 0000000000000000000000000000000000000000..a9b3b8a5218432d8025d952f340a4cb6e10b9e76 --- /dev/null +++ b/src/main/kotlin/com/s3ai/corporate_app2/jms/JmsConsumer.kt @@ -0,0 +1,63 @@ +package com.s3ai.corporate_app2.jms + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import com.s3ai.corporate_app2.jms.persistance.JMSAction +import com.s3ai.corporate_app2.jms.persistance.JMSActionRepository +import com.s3ai.corporate_app2.jms.persistance.SubscriptionTypeRepository +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.jms.annotation.JmsListener +import org.springframework.mail.javamail.JavaMailSender +import org.springframework.mail.javamail.MimeMessageHelper +import org.springframework.messaging.MessagingException +import org.springframework.stereotype.Component + + +@Component +class JmsConsumer { + + @Value("\${spring.mail.username}") + private lateinit var sourceUser: String + + @Autowired + private lateinit var jmsActionService: JMSActionRepository + + @Autowired + private lateinit var sender: JavaMailSender + + @Autowired + private lateinit var subTypeRepo: SubscriptionTypeRepository + + private final val mapper = jacksonObjectMapper() + + fun sendEmail(action: JMSAction, targetUsers: Array<String>): Boolean { + val message = sender.createMimeMessage() + val helper = MimeMessageHelper(message) + try { + helper.setFrom(sourceUser) + helper.setTo(targetUsers) + helper.setText("Entity ${action.entity} was ${action.actionJMS.str}. ${action.actionDescription}") + helper.setSubject("Cinemas service notification") + sender.send(message) + } catch (e: MessagingException) { + e.printStackTrace() + return false + } + return true + + } + + fun notifySubscribers(action: JMSAction) { + val users = subTypeRepo.findSubscriptionTypeByType(action.actionJMS) + val emails = users.map { u -> u.subscriber.email!! }.distinct().toTypedArray() + if (emails.isNotEmpty()) sendEmail(action, emails) + } + + @JmsListener(destination = "cinemas.queue") + fun consume(action_text: String) { + val action: JMSAction = mapper.readValue(action_text) + jmsActionService.save(action) + notifySubscribers(action) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/jms/persistance/entities.kt b/src/main/kotlin/com/s3ai/corporate_app2/jms/persistance/entities.kt new file mode 100644 index 0000000000000000000000000000000000000000..d92945381ec8df3b3ddcff3c93c68415495ac873 --- /dev/null +++ b/src/main/kotlin/com/s3ai/corporate_app2/jms/persistance/entities.kt @@ -0,0 +1,75 @@ +package com.s3ai.corporate_app2.jms.persistance + +import com.fasterxml.jackson.annotation.JsonAutoDetect +import org.hibernate.annotations.CreationTimestamp +import org.hibernate.annotations.OnDelete +import org.hibernate.annotations.OnDeleteAction +import java.time.LocalDateTime +import java.util.* +import javax.persistence.* + + +enum class JMSEntityAction(val str: String) { + UPDATE("updated"), + QUERY("queried"), + CREATE("created"), + DELETE("deleted") +} + +@Entity +@Table(name = "jms_action") +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) +class JMSAction { + @Id + @Column(name = "ID") + @GeneratedValue(strategy = GenerationType.AUTO) + var id: UUID? = null + @Enumerated(EnumType.STRING) + @Column(length = 8, name = "action") + lateinit var actionJMS: JMSEntityAction + @Column(name = "entity") + lateinit var entity: String + @Column(name = "description", columnDefinition = "TEXT") + lateinit var actionDescription: String + @CreationTimestamp + private val createDateTime: LocalDateTime? = null + @Column(name = "delivered") + var delivered: Boolean = true + + override fun toString(): String { + return "JMSAction{id=$id, action=$actionJMS, entity='$entity', actionDescription='$actionDescription'}" + } +} + +@Entity +@Table(name = "subscriber") +class Subscriber { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + var id: UUID? = null + + @Column(name = "email", unique = true) + var email: String? = null + + @CreationTimestamp + private val createDateTime: LocalDateTime? = null + + @OneToMany + var types: List<SubscriptionType> = mutableListOf() +} + +@Entity +@Table(name = "subscription_type") +class SubscriptionType { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + var id: Long? = null + + @Enumerated(EnumType.STRING) + @Column(length = 8, name = "type") + lateinit var type: JMSEntityAction + + @OneToOne + @OnDelete(action = OnDeleteAction.CASCADE) + lateinit var subscriber: Subscriber +} \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/jms/persistance/repositories.kt b/src/main/kotlin/com/s3ai/corporate_app2/jms/persistance/repositories.kt new file mode 100644 index 0000000000000000000000000000000000000000..d33f03567beaa9979924620d23ba187a21152d81 --- /dev/null +++ b/src/main/kotlin/com/s3ai/corporate_app2/jms/persistance/repositories.kt @@ -0,0 +1,25 @@ +package com.s3ai.corporate_app2.jms.persistance + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.query.Param +import org.springframework.stereotype.Repository +import java.util.* + +@Repository +interface JMSActionRepository : JpaRepository<JMSAction, UUID> { + @Query(value = "SELECT * FROM public.jms_action WHERE delivered = FALSE", nativeQuery = true) + fun findUndelivered(): MutableList<JMSAction> +} + +@Repository +interface SubscribersRepository : JpaRepository<Subscriber, UUID> { + @Query("FROM Subscriber WHERE email = :email") + fun findSubscriberByEmail(@Param("email") email: String): Optional<Subscriber> +} + +@Repository +interface SubscriptionTypeRepository : JpaRepository<SubscriptionType, Long> { + @Query("FROM SubscriptionType WHERE type = :type") + fun findSubscriptionTypeByType(@Param("type") type: JMSEntityAction): List<SubscriptionType> +} \ No newline at end of file diff --git a/src/main/kotlin/com/s3ai/corporate_app2/repositories.kt b/src/main/kotlin/com/s3ai/corporate_app2/repositories.kt index 6077d0af27e30232e62504301f20b3cf4d1769ac..3eed32fda03c68a06b4a0df1d5d079b440b13d55 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/repositories.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/repositories.kt @@ -1,6 +1,7 @@ package com.s3ai.corporate_app2 import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository import java.util.* diff --git a/src/main/kotlin/com/s3ai/corporate_app2/services.kt b/src/main/kotlin/com/s3ai/corporate_app2/services.kt index 51cf0fbcd93065f44c259907c8861916d8f35ad9..0f51ab0ed7745a51432beed02dce246764129433 100644 --- a/src/main/kotlin/com/s3ai/corporate_app2/services.kt +++ b/src/main/kotlin/com/s3ai/corporate_app2/services.kt @@ -1,5 +1,6 @@ package com.s3ai.corporate_app2 +import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Service import java.util.* @@ -9,6 +10,7 @@ interface CinemaServices<T> { fun findById(id: UUID): T? fun save(item: T): T fun delete(item: T) + fun itemName(): String } @Service @@ -17,6 +19,7 @@ class CinemaService(private val cinemaRepository: CinemaRepository) : CinemaServ override fun findById(id: UUID): Cinema? = cinemaRepository.findById(id).orElse(null) override fun save(item: Cinema): Cinema = cinemaRepository.save(item) override fun delete(item: Cinema) = cinemaRepository.delete(item) + override fun itemName(): String = "Cinema" } @Service @@ -25,6 +28,7 @@ class UserService(private val userRepository: UserRepository) : CinemaServices<U override fun findById(id: UUID): User? = userRepository.findById(id).orElse(null) override fun save(item: User): User = userRepository.save(item) override fun delete(item: User) = userRepository.delete(item) + override fun itemName(): String = "User" } @Service @@ -33,4 +37,5 @@ class TicketService(private val ticketRepository: TicketRepository) : CinemaServ override fun findById(id: UUID): Ticket? = ticketRepository.findById(id).orElse(null) override fun save(item: Ticket): Ticket = ticketRepository.save(item) override fun delete(item: Ticket) = ticketRepository.delete(item) + override fun itemName(): String = "Ticket" } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 2b046747b6b61873c2a783b6c5240ea6750b4f25..174500c59db2965eb5978115e1fd81579a9e8390 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,10 +1,31 @@ spring.thymeleaf.cache=false + spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://corporate_db2:5432/cinema spring.datasource.username=postgres spring.datasource.password=postgres spring.datasource.platform=postgres + spring.jpa.hibernate.ddl-auto=update spring.jpa.database=postgresql spring.jpa.database-platform=postgres -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect \ No newline at end of file +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect + +spring.activemq.in-memory=false +spring.activemq.pool.enabled=true +spring.activemq.broker-url=tcp://actvemq:61616 +spring.activemq.user=admin +spring.activemq.password=admin + + +spring.mail.port=465 +spring.mail.host=smtp.yandex.com +spring.mail.username=username +spring.mail.password=password + +spring.mail.properties.mail.smtp.starttls.enable=true +spring.mail.properties.mail.smtp.auth = true +spring.mail.properties.mail.smtp.socketFactory.port = 465 +spring.mail.properties.mail.smtp.socketFactory.class = javax.net.ssl.SSLSocketFactory +spring.mail.properties.mail.smtp.socketFactory.fallback = false +spring.mail.properties.mail.smtp.ssl.enable=false \ No newline at end of file diff --git a/src/main/resources/static/css/index.css b/src/main/resources/static/css/index.css index debb6d5093a78a712339848412fe77c5f4ad9378..2b1e279bc8a5e9ae24160b4489e0a8dcbc12ba5a 100644 --- a/src/main/resources/static/css/index.css +++ b/src/main/resources/static/css/index.css @@ -1 +1 @@ -.index-gradient{background:linear-gradient(to bottom, #283048 0%, #859398 100%)}.projects-section{padding:10rem 0}.projects-section .featured-text{padding:2rem}@media(min-width: 992px){.projects-section .featured-text{padding:0 0 0 2rem;border-left:.5rem solid #64a19d}}.projects-section .project-text{padding:3rem;font-size:90%}@media(min-width: 992px){.projects-section .project-text{padding:5rem}.projects-section .project-text hr{border-color:#64a19d;border-width:.25rem;width:30%}}.masthead{object-fit:cover !important;text-align:center !important;background-repeat:no-repeat !important;background-attachment:fixed !important;background-position:center !important}.masthead .white{color:#fff}.hide-y{overflow-y:hidden}.landing-info-row{height:100px}.landing-info-row .landing-info-image{height:100px;object-fit:cover;width:100%;text-align:center;background-repeat:no-repeat;background-attachment:fixed;background-position:center}.masthead{position:relative;width:100%;height:auto;min-height:35rem;padding:15rem 0;background-position:center;background-repeat:no-repeat;background-attachment:scroll;background-size:cover}.masthead h1{font-family:"Varela Round";font-size:2.5rem;line-height:2.5rem;letter-spacing:.8rem;background:-webkit-linear-gradient(rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0));-webkit-text-fill-color:transparent;-webkit-background-clip:text}.masthead h2{max-width:20rem;font-size:1rem}@media(min-width: 768px){.masthead h1{font-size:4rem;line-height:4rem}}@media(min-width: 992px){.masthead{height:100vh;padding:0}.masthead h1{font-size:6.5rem;line-height:6.5rem;letter-spacing:.8rem}.masthead h2{max-width:30rem;font-size:1.25rem}}.contact-section{padding:5rem 0 0}.contact-section .card{border:0;border-bottom:.25rem solid #64a19d}.contact-section .card h4{font-size:.8rem;font-family:"Varela Round";text-transform:uppercase;letter-spacing:.15rem}.contact-section .card hr{border-color:#64a19d;border-width:.25rem;width:3rem}.contact-section .social{margin-top:5rem}.contact-section .social a{text-align:center;height:3rem;width:3rem;background:rgba(255,255,255,.1);border-radius:100%;line-height:3rem;color:rgba(255,255,255,.3)}.contact-section .social a:hover{color:rgba(255,255,255,.5)}.contact-section .social a:active{color:#fff}.btn{box-shadow:0 .1875rem .1875rem 0 rgba(0,0,0,.1) !important;padding:1.25rem 2rem;font-family:"Varela Round";font-size:80%;text-transform:uppercase;letter-spacing:.15rem;border:0}.btn-interact{background-color:#64a19d}.btn-interact:hover{background-color:#4f837f}.btn-interact:focus{background-color:#4f837f;color:#fff}.btn-interact:active{background-color:#467370 !important}.about-section{padding-top:10rem}.about-section p{margin-bottom:5rem}/*# sourceMappingURL=index.css.map */ +.index-gradient{background:linear-gradient(to bottom, #283048 0%, #859398 100%)}.projects-section{padding:10rem 0}.projects-section .featured-text{padding:2rem}@media(min-width: 992px){.projects-section .featured-text{padding:0 0 0 2rem;border-left:.5rem solid #64a19d}}.projects-section .project-text{padding:3rem;font-size:90%}@media(min-width: 992px){.projects-section .project-text{padding:5rem}.projects-section .project-text hr{border-color:#64a19d;border-width:.25rem;width:30%}}.masthead{object-fit:cover !important;text-align:center !important;background-repeat:no-repeat !important;background-attachment:fixed !important;background-position:center !important}.masthead .white{color:#fff}.hide-y{overflow-y:hidden}.landing-info-row{height:100px}.landing-info-row .landing-info-image{height:100px;object-fit:cover;width:100%;text-align:center;background-repeat:no-repeat;background-attachment:fixed;background-position:center}.masthead{position:relative;width:100%;height:auto;min-height:35rem;padding:15rem 0;background-position:center;background-repeat:no-repeat;background-attachment:scroll;background-size:cover}.masthead h1{font-family:"Varela Round";font-size:2.5rem;line-height:2.5rem;letter-spacing:.8rem;background:-webkit-linear-gradient(rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0));-webkit-text-fill-color:transparent;-webkit-background-clip:text}.masthead h2{max-width:20rem;font-size:1rem}@media(min-width: 768px){.masthead h1{font-size:4rem;line-height:4rem}}@media(min-width: 992px){.masthead{height:100vh;padding:0}.masthead h1{font-size:6.5rem;line-height:6.5rem;letter-spacing:.8rem}.masthead h2{max-width:30rem;font-size:1.25rem}}.contact-section{padding:5rem 0 0}.contact-section .card{border:0;border-bottom:.25rem solid #64a19d}.contact-section .card h4{font-size:.8rem;font-family:"Varela Round";text-transform:uppercase;letter-spacing:.15rem}.contact-section .card hr{border-color:#64a19d;border-width:.25rem;width:3rem}.contact-section .social{margin-top:5rem}.contact-section .social a{text-align:center;height:3rem;width:3rem;background:rgba(255,255,255,.1);border-radius:100%;line-height:3rem;padding-top:1rem;color:rgba(255,255,255,.3)}.contact-section .social a:hover{color:rgba(255,255,255,.5)}.contact-section .social a:active{color:#fff}.btn{box-shadow:0 .1875rem .1875rem 0 rgba(0,0,0,.1) !important;padding:1.25rem 2rem;font-family:"Varela Round";font-size:80%;text-transform:uppercase;letter-spacing:.15rem;border:0}.btn-interact{background-color:#64a19d}.btn-interact:hover{background-color:#4f837f}.btn-interact:focus{background-color:#4f837f;color:#fff}.btn-interact:active{background-color:#467370 !important}.about-section{padding-top:10rem}.about-section p{margin-bottom:5rem}/*# sourceMappingURL=index.css.map */ diff --git a/src/main/resources/static/css/index.css.map b/src/main/resources/static/css/index.css.map index ac5ec28cec5c7830247919fbb40389de5b05cde7..9de368993141b11db8c8f0765e5adb8c455ce2b7 100644 --- a/src/main/resources/static/css/index.css.map +++ b/src/main/resources/static/css/index.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../scss/index.scss","../scss/_variables.scss"],"names":[],"mappings":"AAEA,gBACE,gEAGF,kBACE,gBAEA,iCACE,aACA,yBAFF,iCAGI,mBACA,iCAIJ,gCACE,aACA,cACA,yBAHF,gCAII,aACA,mCACE,aCXE,QDYF,oBACA,WAOR,UACE,4BACA,6BACA,uCACA,uCACA,sCAEA,iBACE,WAKJ,QACE,kBAGF,kBACE,aAEA,sCACE,aACA,iBACA,WACA,kBACA,4BACA,4BACA,2BAIJ,UACE,kBACA,WACA,YACA,iBACA,gBACA,2BACA,4BACA,6BACA,sBAEA,aACE,2BACA,iBACA,mBACA,qBACA,qFACA,oCACA,6BAGF,aACE,gBACA,eAGF,yBACE,aACE,eACA,kBAGJ,yBAhCF,UAiCI,aACA,UACA,aACE,iBACA,mBACA,qBAEF,aACE,gBACA,mBAKN,iBACE,iBAEA,uBACE,SACA,mCAEA,0BACE,gBACA,2BACA,yBACA,sBAGF,0BACE,aCjHI,QDkHJ,oBACA,WAIJ,yBACE,gBAEA,2BACE,kBACA,YACA,WACA,gCACA,mBACA,iBACA,2BAEA,iCACE,2BAGF,kCACE,MCpJA,KD0JR,KACE,2DACA,qBACA,2BACA,cACA,yBACA,sBACA,SAGF,cACE,iBCzJQ,QD2JR,oBACE,yBAGF,oBACE,yBACA,WAGF,qBACE,oCAIJ,eACE,kBAEA,iBACE","file":"index.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../scss/index.scss","../scss/_variables.scss"],"names":[],"mappings":"AAEA,gBACE,gEAGF,kBACE,gBAEA,iCACE,aACA,yBAFF,iCAGI,mBACA,iCAIJ,gCACE,aACA,cACA,yBAHF,gCAII,aACA,mCACE,aCXE,QDYF,oBACA,WAOR,UACE,4BACA,6BACA,uCACA,uCACA,sCAEA,iBACE,WAKJ,QACE,kBAGF,kBACE,aAEA,sCACE,aACA,iBACA,WACA,kBACA,4BACA,4BACA,2BAIJ,UACE,kBACA,WACA,YACA,iBACA,gBACA,2BACA,4BACA,6BACA,sBAEA,aACE,2BACA,iBACA,mBACA,qBACA,qFACA,oCACA,6BAGF,aACE,gBACA,eAGF,yBACE,aACE,eACA,kBAGJ,yBAhCF,UAiCI,aACA,UACA,aACE,iBACA,mBACA,qBAEF,aACE,gBACA,mBAKN,iBACE,iBAEA,uBACE,SACA,mCAEA,0BACE,gBACA,2BACA,yBACA,sBAGF,0BACE,aCjHI,QDkHJ,oBACA,WAIJ,yBACE,gBAEA,2BACE,kBACA,YACA,WACA,gCACA,mBACA,iBACA,iBACA,2BAEA,iCACE,2BAGF,kCACE,MCrJA,KD2JR,KACE,2DACA,qBACA,2BACA,cACA,yBACA,sBACA,SAGF,cACE,iBC1JQ,QD4JR,oBACE,yBAGF,oBACE,yBACA,WAGF,qBACE,oCAIJ,eACE,kBAEA,iBACE","file":"index.css"} \ No newline at end of file diff --git a/src/main/resources/static/css/subscribes.css b/src/main/resources/static/css/subscribes.css new file mode 100644 index 0000000000000000000000000000000000000000..bddef40a2197a7b43b64c0b9862bb535176e25a8 --- /dev/null +++ b/src/main/resources/static/css/subscribes.css @@ -0,0 +1 @@ +.editing .container{height:100%;display:flex;justify-content:center;align-items:center}.table-padding{padding-top:56px}/*# sourceMappingURL=subscribes.css.map */ diff --git a/src/main/resources/static/css/subscribes.css.map b/src/main/resources/static/css/subscribes.css.map new file mode 100644 index 0000000000000000000000000000000000000000..ca13f09148a7e341d71224b04abdbe028a09bfca --- /dev/null +++ b/src/main/resources/static/css/subscribes.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["../scss/cinemas.scss"],"names":[],"mappings":"AAEE,oBACE,YACA,aACA,uBACA,mBAIJ,eACE","file":"subscribes.css"} \ No newline at end of file diff --git a/src/main/resources/static/images/email.jpg b/src/main/resources/static/images/email.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1c0b5ad82dd76e3e7c7ad4c3f1e1e7ff8a2dcfa0 Binary files /dev/null and b/src/main/resources/static/images/email.jpg differ diff --git a/src/main/resources/static/scss/index.scss b/src/main/resources/static/scss/index.scss index 9cd61da7fde0cfeb33ccd958c7eafeecebf4dd4a..eca5788b5a44fa6f3c2588a6fe73a32fce11d3e4 100644 --- a/src/main/resources/static/scss/index.scss +++ b/src/main/resources/static/scss/index.scss @@ -139,6 +139,7 @@ background: fade-out($white, 0.9); border-radius: 100%; line-height: 3rem; + padding-top: 1rem; color: fade-out($white, 0.7); &:hover { diff --git a/src/main/resources/static/scss/subscribes.scss b/src/main/resources/static/scss/subscribes.scss new file mode 100644 index 0000000000000000000000000000000000000000..5743cc609b56787d8ce5ee9d9d10e56fbdeaf990 --- /dev/null +++ b/src/main/resources/static/scss/subscribes.scss @@ -0,0 +1 @@ +@import "cinemas"; \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 81d4dfdba74410001268d4b148a786847b4ae90a..f61ebdb980d0fd115da98fa7d81fda45b34bd5cd 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -29,7 +29,8 @@ <h2 class="text-white mb-4">Built for cinema business. Built with passion for movies.</h2> <p class="text-white-50"> We are glad to provide you the best possible instrument to manage your cinema network. - Use it to control your cinemas, track tickets to movies, and gather precious data for your business. + Use it to control your cinemas, track tickets to movies, and gather precious data for your + business. System is established to be user-friendly and easy to get on. </p> </div> @@ -80,7 +81,7 @@ </div> <!-- Project Two Row --> - <div class="row justify-content-center hide-y no-gutters"> + <div class="row justify-content-center hide-y no-gutters mt-3"> <div class="col-lg-6"> <img class="pl-3 img-fluid landing-info-image" src="/images/landing_users.jpg" alt=""> @@ -91,7 +92,8 @@ <div class="project-text w-100 my-auto text-center text-lg-left"> <h4 class="text-white">User management</h4> <p class="mb-0 text-white-50"> - Build up your customers database. Track popularity of certain titles and places basing + Build up your customers database. Track popularity of certain titles and places + basing on precious personal data of innocent people, and enjoy the world we live in. </p> <hr class="d-none d-lg-block mb-0 mr-0"> @@ -101,12 +103,36 @@ </div> </div> + <div class="row justify-content-center no-gutters mb-5 mt-3 mb-lg-0"> + <div class="col-lg-6"> + <img class="img-fluid landing-info-image pr-3" + src="/images/email.jpg" alt=""> + </div> + <div class="col-lg-6 order-lg-first"> + <div class="bg-black text-center h-100 project"> + <div class="d-flex h-100"> + <div class="project-text w-100 my-auto text-center text-lg-right"> + <h4 class="text-white">Updates subscription</h4> + <p class="mb-0 text-white-50"> + Stay up to date with the latest site updates. + Track your database changes with only your email. + All updates will be instantly sent to you. </p> + </div> + </div> + </div> + </div> + </div> </div> </section> </div> - +<section class="subscribe-section pt-3 bg-dark"> + <div class="row justify-content-center"> + <a class="btn btn-light mr-2" href="/subscription/subscribe">Subscribe</a> + <a class="btn btn-light ml-2" href="/subscription/unsubscribe">Unsubscribe</a> + </div> +</section> <!-- Contact Section --> -<section class="contact-section mt-3 bg-black"> +<section class="contact-section pt-3 bg-dark"> <div class="container"> <div class="row"> @@ -158,7 +184,7 @@ <a href="#" class="mx-2"> <i class="fab fa-vk"></i> </a> - <a href="https://gitlab.le-memese.com/s3rius/corporative_systems" class="mx-2"> + <a href="https://gitlab.le-memese.com/s3rius/corporative_systems2" class="mx-2"> <i class="fab fa-gitlab"></i> </a> </div> @@ -167,7 +193,7 @@ </section> <!-- Footer --> -<footer class="bg-black small text-center text-black-50"> +<footer class="bg-dark small text-center text-white-50"> <div class="container"> Copyright © le-memese 2019 </div> diff --git a/src/main/resources/templates/subscription/subscribe.html b/src/main/resources/templates/subscription/subscribe.html new file mode 100644 index 0000000000000000000000000000000000000000..a66314e387a21812513695d810f5a711f31abaec --- /dev/null +++ b/src/main/resources/templates/subscription/subscribe.html @@ -0,0 +1,61 @@ +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <title>Edit cinema</title> + <head th:include="commons/imports"></head> + <link rel="stylesheet" href="/css/subscribes.css"> + <script src="/js/subscribe.js"></script> +</head> +<body class="themed-gradient"> + +<!-- Navigation --> +<body th:include="commons/header"></body> + +<div class="editing"> + <div class="container justify-content-center"> + + <form id="cinemaForm" class="d-flex justify-content-center" th:object="${subscriber}" method="post" + action="/subscription/subscribe"> + <fieldset> + + <legend> + <div class="text-center"><h2><b class="white">Subscribe to updates</b></h2></div> + </legend> + <br> + <input type="hidden" name="id" th:field="*{id}"> + + <div class="form-group"> + <div class="inputGroupContainer"> + <div class="input-group"> + <div class="input-group-prepend"> + <div class="input-group-text"><i class="fas fa-envelope"></i></div> + </div> + <input type="text" + class="form-control validate" + id="Email" + name="email" + placeholder="User's email" th:field="*{email}" required> + </div> + </div> + </div> + <div class="form-group"> + <div class="inputGroupContainer"> + <div class="input-group"> +<!-- <div class="input-group-prepend">--> +<!-- <div class="input-group-text"><i class="fas fa-chair"></i></div>--> +<!-- </div>--> + <div class="form-check" th:each="item: ${types}"> + <input type="checkbox" class="form-check-input" name="actions" th:value="${item}"> + <label th:text="${item.str}" class="text-white pr-2"></label> + </div> + </div> + </div> + </div> + <div class="justify-content-center"> + <button type="submit" class="btn btn-primary w-100"><i class="fas fa-check"></i>Submit</button> + </div> + </fieldset> + </form> + </div> +</div> +</body> +</html> diff --git a/src/main/resources/templates/subscription/unsubscribe.html b/src/main/resources/templates/subscription/unsubscribe.html new file mode 100644 index 0000000000000000000000000000000000000000..3e625399c170d1a15bf9e121a62e38cb0d92e35b --- /dev/null +++ b/src/main/resources/templates/subscription/unsubscribe.html @@ -0,0 +1,46 @@ +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <title>Edit cinema</title> + <head th:include="commons/imports"></head> + <link rel="stylesheet" href="/css/subscribes.css"> + <script src="/js/subscribe.js"></script> +</head> +<body class="themed-gradient"> + +<!-- Navigation --> +<body th:include="commons/header"></body> + +<div class="editing"> + <div class="container justify-content-center"> + + <form id="cinemaForm" class="d-flex justify-content-center" method="post" + action="/subscription/unsubscribe"> + <fieldset> + + <legend> + <div class="text-center"><h2><b class="white">Subscribe to updates</b></h2></div> + </legend> + <br> + + <div class="form-group"> + <div class="inputGroupContainer"> + <div class="input-group"> + <div class="input-group-prepend"> + <div class="input-group-text"><i class="fas fa-envelope"></i></div> + </div> + <input type="text" + class="form-control validate" + id="Email" + placeholder="User's email" name="email" required> + </div> + </div> + </div> + <div class="justify-content-center"> + <button type="submit" class="btn btn-primary w-100"><i class="fas fa-check"></i>Submit</button> + </div> + </fieldset> + </form> + </div> +</div> +</body> +</html>