diff --git a/effectiveOfficeBackend/src/main/kotlin/office/effective/common/constants/BookingConstants.kt b/effectiveOfficeBackend/src/main/kotlin/office/effective/common/constants/BookingConstants.kt index b7658ecf5..258162909 100644 --- a/effectiveOfficeBackend/src/main/kotlin/office/effective/common/constants/BookingConstants.kt +++ b/effectiveOfficeBackend/src/main/kotlin/office/effective/common/constants/BookingConstants.kt @@ -18,4 +18,6 @@ object BookingConstants { val WORKSPACE_CALENDAR: String = System.getenv("WORKSPACE_CALENDAR") ?: config.propertyOrNull("calendar.workspaceCalendar")?.getString() ?: throw Exception("Environment and config file does not contain workspace Google calendar id") + + const val UNTIL_FORMAT = "yyyyMMdd" } diff --git a/effectiveOfficeBackend/src/main/kotlin/office/effective/common/di/CommonDiModule.kt b/effectiveOfficeBackend/src/main/kotlin/office/effective/common/di/CommonDiModule.kt index b6a5d1400..073f03f86 100644 --- a/effectiveOfficeBackend/src/main/kotlin/office/effective/common/di/CommonDiModule.kt +++ b/effectiveOfficeBackend/src/main/kotlin/office/effective/common/di/CommonDiModule.kt @@ -1,6 +1,7 @@ package office.effective.common.di import office.effective.common.utils.DatabaseTransactionManager +import office.effective.common.utils.impl.DatabaseTransactionManagerImpl import office.effective.common.utils.UuidValidator import org.koin.dsl.module import org.ktorm.database.Database @@ -22,6 +23,6 @@ val commonDiModule = module(createdAtStart = true) { password = password ) } - single { DatabaseTransactionManager(get()) } + single { DatabaseTransactionManagerImpl(get()) } single { UuidValidator() } } diff --git a/effectiveOfficeBackend/src/main/kotlin/office/effective/common/utils/DatabaseTransactionManager.kt b/effectiveOfficeBackend/src/main/kotlin/office/effective/common/utils/DatabaseTransactionManager.kt index b0eb9be19..afb0b4b7d 100644 --- a/effectiveOfficeBackend/src/main/kotlin/office/effective/common/utils/DatabaseTransactionManager.kt +++ b/effectiveOfficeBackend/src/main/kotlin/office/effective/common/utils/DatabaseTransactionManager.kt @@ -1,20 +1,11 @@ -package office.effective.common.utils +package office.effective.common.utils; -import org.ktorm.database.Database -import org.ktorm.database.TransactionIsolation -import org.ktorm.database.TransactionManager -import org.slf4j.LoggerFactory +import org.ktorm.database.TransactionIsolation; /** - * Class used for creation database transaction on the facade layer + * Interface for classes used for creation database transaction on the facade layer */ -class DatabaseTransactionManager(database: Database) { - private val transactionManager: TransactionManager - private val logger = LoggerFactory.getLogger(this::class.java) - init { - transactionManager = database.transactionManager - } - +interface DatabaseTransactionManager { /** * Executes code in a database transaction. * @@ -23,22 +14,5 @@ class DatabaseTransactionManager(database: Database) { * @param serviceCall lambda function to be executed * @param isolation transaction isolation */ - fun useTransaction(serviceCall: () -> T, - isolation: TransactionIsolation = TransactionIsolation.READ_COMMITTED): T { - logger.debug("Opening new database {} transaction", isolation.name) - val transaction = transactionManager.newTransaction(isolation = isolation) - val result: T - try { - result = serviceCall() - transaction.commit() - logger.debug("Transaction committed") - } catch (e: Throwable) { - logger.debug("Rollback transaction") - transaction.rollback() - throw e - } finally { - transaction.close() - } - return result - } + fun useTransaction(serviceCall: () -> T, isolation: TransactionIsolation = TransactionIsolation.READ_COMMITTED): T } diff --git a/effectiveOfficeBackend/src/main/kotlin/office/effective/common/utils/impl/DatabaseTransactionManagerImpl.kt b/effectiveOfficeBackend/src/main/kotlin/office/effective/common/utils/impl/DatabaseTransactionManagerImpl.kt new file mode 100644 index 000000000..07df81593 --- /dev/null +++ b/effectiveOfficeBackend/src/main/kotlin/office/effective/common/utils/impl/DatabaseTransactionManagerImpl.kt @@ -0,0 +1,41 @@ +package office.effective.common.utils.impl + +import office.effective.common.utils.DatabaseTransactionManager +import org.ktorm.database.Database +import org.ktorm.database.TransactionIsolation +import org.ktorm.database.TransactionManager +import org.slf4j.LoggerFactory + +/** + * Class used for creation database transaction on the facade layer + */ +class DatabaseTransactionManagerImpl(database: Database) : DatabaseTransactionManager { + private val transactionManager: TransactionManager = database.transactionManager + private val logger = LoggerFactory.getLogger(this::class.java) + + /** + * Executes code in a database transaction. + * + * Rollbacks the transaction if an exception was thrown. + * + * @param serviceCall lambda function to be executed + * @param isolation transaction isolation + */ + override fun useTransaction(serviceCall: () -> T, isolation: TransactionIsolation): T { + logger.debug("Opening new database {} transaction", isolation.name) + val transaction = transactionManager.newTransaction(isolation = isolation) + val result: T + try { + result = serviceCall() + transaction.commit() + logger.debug("Transaction committed") + } catch (e: Throwable) { + logger.debug("Rollback transaction") + transaction.rollback() + throw e + } finally { + transaction.close() + } + return result + } +} diff --git a/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/converters/GoogleCalendarConverter.kt b/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/converters/GoogleCalendarConverter.kt index ef7878439..0c87bd629 100644 --- a/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/converters/GoogleCalendarConverter.kt +++ b/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/converters/GoogleCalendarConverter.kt @@ -5,7 +5,7 @@ import com.google.api.services.calendar.model.Event import com.google.api.services.calendar.model.Event.Organizer import com.google.api.services.calendar.model.EventAttendee import com.google.api.services.calendar.model.EventDateTime -import model.Recurrence.Companion.toRecurrence +import office.effective.model.Recurrence.Companion.toRecurrence import office.effective.common.constants.BookingConstants import office.effective.common.utils.UuidValidator import office.effective.dto.BookingDTO @@ -184,9 +184,8 @@ class GoogleCalendarConverter( fun toGoogleEvent(dto: BookingDTO): Event { logger.debug("[toGoogleEvent] converting meeting room booking dto to calendar event") val event = Event().apply { - summary = "${dto.owner.fullName}: create from office application" - description = - "${dto.owner.email} - почта организатора"//"${dto.owner.integrations?.first { it.name == "email" }} - почта организатора" + summary = createDetailedEventSummary(dto) + description = "${dto.owner.email} - почта организатора" organizer = dto.owner.toGoogleOrganizer() attendees = dto.participants.map { it.toAttendee() } + dto.owner.toAttendee() .apply { organizer = true } + dto.workspace.toAttendee() @@ -264,6 +263,10 @@ class GoogleCalendarConverter( return bookingConverter.dtoToModel(toBookingDTO(event)); } + private fun createDetailedEventSummary(dto: BookingDTO): String { + return "Meet ${dto.owner.fullName}" + } + /** * Converts milliseconds to [EventDateTime] * @@ -272,7 +275,7 @@ class GoogleCalendarConverter( */ private fun Long.toGoogleDateTime(): EventDateTime { return EventDateTime().also { - it.dateTime = DateTime(this) + it.dateTime = DateTime(this - TimeZone.getDefault().rawOffset) it.timeZone = TimeZone.getDefault().id } } diff --git a/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/converters/RecurrenceRuleFactory.kt b/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/converters/RecurrenceRuleFactory.kt index 7858c5e38..b41c7e3ef 100644 --- a/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/converters/RecurrenceRuleFactory.kt +++ b/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/converters/RecurrenceRuleFactory.kt @@ -1,11 +1,8 @@ package office.effective.features.booking.converters -import model.Ending -import model.Freq -import model.Recurrence -import java.text.SimpleDateFormat -import java.util.Formatter -import java.util.GregorianCalendar +import office.effective.model.Ending +import office.effective.model.Freq +import office.effective.model.Recurrence /** * Object for creating Google calendar recurrence rule diff --git a/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/repository/BookingCalendarRepository.kt b/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/repository/BookingCalendarRepository.kt index f5e39f068..3144c96cc 100644 --- a/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/repository/BookingCalendarRepository.kt +++ b/effectiveOfficeBackend/src/main/kotlin/office/effective/features/booking/repository/BookingCalendarRepository.kt @@ -4,6 +4,7 @@ import com.google.api.client.googleapis.json.GoogleJsonResponseException import com.google.api.client.util.DateTime import com.google.api.services.calendar.Calendar import com.google.api.services.calendar.model.Event +import office.effective.common.constants.BookingConstants import office.effective.common.exception.InstanceNotFoundException import office.effective.common.exception.MissingIdException import office.effective.common.exception.WorkspaceUnavailableException @@ -29,8 +30,7 @@ class BookingCalendarRepository( private val googleCalendarConverter: GoogleCalendarConverter ) : IBookingRepository { private val calendarEvents = calendar.Events() - private val defaultCalendar = config.propertyOrNull("calendar.defaultCalendar")?.getString() - ?: throw Exception("Config file does not contain Google default calendar id") + private val defaultCalendar = BookingConstants.DEFAULT_CALENDAR private val logger = LoggerFactory.getLogger(this::class.java) /** diff --git a/effectiveOfficeBackend/src/main/kotlin/office/effective/model/Recurrence.kt b/effectiveOfficeBackend/src/main/kotlin/office/effective/model/Recurrence.kt index 88c26dabc..943e5ae66 100644 --- a/effectiveOfficeBackend/src/main/kotlin/office/effective/model/Recurrence.kt +++ b/effectiveOfficeBackend/src/main/kotlin/office/effective/model/Recurrence.kt @@ -1,8 +1,8 @@ -package model +package office.effective.model -import com.google.api.client.util.DateTime +import model.RecurrenceDTO +import office.effective.common.constants.BookingConstants import java.lang.IllegalArgumentException -import java.lang.StringBuilder import java.text.SimpleDateFormat import java.util.* @@ -45,40 +45,29 @@ data class Recurrence( * Converts milliseconds date into exdate format from RFC5545 * * @param millisDate - date in milliseconds ([Long]) - * @return [String] - date in DATE-TIME (RFC5545). Example: 20231015T200000Z + * @return [String] - date in DATE-TIME (RFC5545). Example: [BookingConstants.UNTIL_FORMAT] * @author Kiselev Danil * */ private fun toDateRfc5545(millisDate: Long): String { - val time = GregorianCalendar().apply { timeInMillis = millisDate } - return SimpleDateFormat("yyyyMMdd").format(time.time) + val time = GregorianCalendar().apply { timeInMillis = millisDate + 86400000 } + return SimpleDateFormat(BookingConstants.UNTIL_FORMAT).format(time.time) } } - fun toDto(): RecurrenceDTO = RecurrenceDTO( + public fun toDto(): RecurrenceDTO = RecurrenceDTO( interval = if (interval != 0) interval else null, freq = freq.name, count = if (ending is Ending.Count) ending.value else null, - until = if (ending is Ending.Until) parceUntil(ending.value) else null, + until = if (ending is Ending.Until) parseUntil(ending.value) else null, byDay = byDay, byMonth = byMonth, byYearDay = byYearDay, byHour = byHour ) - private fun parceUntil(untilStr: String): Long { - //old - //2023 10 15 T 20 00 00 Z - //0123 45 67 8 90 12 34 5 - - //new - // 2023 12 24 - - - val year: Int = untilStr.substring(0,4).toInt() - val month: Int = untilStr.substring(4,6).toInt() - val day: Int = untilStr.substring(6,8).toInt() - - val dt = Date(year - 1900, month, day) - return dt.time + private fun parseUntil(untilStr: String): Long { + val date: Date = SimpleDateFormat(BookingConstants.UNTIL_FORMAT).parse(untilStr) + println("UNTIL: $untilStr") + return date.time } } \ No newline at end of file diff --git a/effectiveOfficeBackend/src/test/kotlin/office/effective/booking/BookingFacadeTest.kt b/effectiveOfficeBackend/src/test/kotlin/office/effective/booking/BookingFacadeTest.kt index 824e3e98b..6a4cea28a 100644 --- a/effectiveOfficeBackend/src/test/kotlin/office/effective/booking/BookingFacadeTest.kt +++ b/effectiveOfficeBackend/src/test/kotlin/office/effective/booking/BookingFacadeTest.kt @@ -2,7 +2,7 @@ package office.effective.booking import junit.framework.TestCase.assertEquals import office.effective.common.exception.InstanceNotFoundException -import office.effective.common.utils.DatabaseTransactionManager +import office.effective.common.utils.impl.DatabaseTransactionManagerImpl import office.effective.common.utils.UuidValidator import office.effective.features.booking.converters.BookingFacadeConverter import office.effective.dto.BookingDTO @@ -33,7 +33,7 @@ class BookingFacadeTest { @Mock private lateinit var uuidValidator: UuidValidator @Mock - private lateinit var transactionManager: DatabaseTransactionManager + private lateinit var transactionManager: DatabaseTransactionManagerImpl @Mock private lateinit var bookingFacadeConverter: BookingFacadeConverter @Mock @@ -75,19 +75,20 @@ class BookingFacadeTest { @Test fun testFindById() { - val bookingId = UUID.fromString("8896abc1-457b-41e4-b80b-2fe7cfb3dbaf") val instant = Instant.now() val existingBooking = Booking( user, emptyList(), workspace, - bookingId, + "8896abc1-457b-41e4-b80b-2fe7cfb3dbaf", + instant, instant, - instant) + null) val expectedBookingDTO = BookingDTO( - userDto, emptyList(), + userDto, + emptyList(), workspaceDto, - bookingId.toString(), + "8896abc1-457b-41e4-b80b-2fe7cfb3dbaf", instant.toEpochMilli(), instant.toEpochMilli()) @@ -95,7 +96,7 @@ class BookingFacadeTest { whenever(service.findById(anyOrNull())).thenReturn(existingBooking) setUpMockConverter(existingBooking, expectedBookingDTO) - val result = facade.findById(bookingId.toString()) + val result = facade.findById("8896abc1-457b-41e4-b80b-2fe7cfb3dbaf") assertEquals(expectedBookingDTO, result) } @@ -118,12 +119,14 @@ class BookingFacadeTest { val expectedList = listOf(bookingMockDto, bookingMockDto) setUpMockTransactionManager() - whenever(service.findAll(anyOrNull(), anyOrNull())).thenReturn(existingList) + whenever(service.findAll(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())).thenReturn(existingList) whenever(bookingFacadeConverter.modelToDto(anyOrNull())).thenReturn(expectedList[0], expectedList[1]) val result = facade.findAll( "8896abc1-457b-41e4-b80b-2fe7cfb3dbaf", - "8896abc2-457b-41e4-b80b-2fe7cfb3dbaf" + "8896abc2-457b-41e4-b80b-2fe7cfb3dbaf", + null, + Instant.now().toEpochMilli() ) assertEquals(expectedList, result) @@ -146,14 +149,22 @@ class BookingFacadeTest { @Test fun testPut() { setUpMockTransactionManager() + val instant = Instant.now() + val expectedBookingDTO = BookingDTO( + userDto, + emptyList(), + workspaceDto, + "8896abc1-457b-41e4-b80b-2fe7cfb3dbaf", + instant.toEpochMilli(), + instant.toEpochMilli()) val resultMockDto = mock() val resultMock = mock() - whenever(bookingFacadeConverter.dtoToModel(bookingMockDto)).thenReturn(bookingMock) + whenever(bookingFacadeConverter.dtoToModel(expectedBookingDTO)).thenReturn(bookingMock) whenever(service.update(bookingMock)).thenReturn(resultMock) - whenever(bookingFacadeConverter.modelToDto(resultMock)).thenReturn(resultMockDto) + whenever(bookingFacadeConverter.modelToDto(resultMock)).thenReturn(expectedBookingDTO) - val result = facade.put(bookingMockDto) + val result = facade.put(expectedBookingDTO) - assertEquals(resultMockDto, result) + assertEquals(expectedBookingDTO, result) } } diff --git a/effectiveOfficeBackend/src/test/kotlin/office/effective/workspace/WorkspaceFacadeTest.kt b/effectiveOfficeBackend/src/test/kotlin/office/effective/workspace/WorkspaceFacadeTest.kt index a243c0882..77e6c35ee 100644 --- a/effectiveOfficeBackend/src/test/kotlin/office/effective/workspace/WorkspaceFacadeTest.kt +++ b/effectiveOfficeBackend/src/test/kotlin/office/effective/workspace/WorkspaceFacadeTest.kt @@ -2,7 +2,7 @@ package office.effective.workspace import junit.framework.TestCase.assertEquals import office.effective.common.exception.InstanceNotFoundException -import office.effective.common.utils.DatabaseTransactionManager +import office.effective.common.utils.impl.DatabaseTransactionManagerImpl import office.effective.common.utils.UuidValidator import office.effective.features.workspace.converters.WorkspaceFacadeConverter import office.effective.dto.WorkspaceDTO @@ -28,7 +28,7 @@ class WorkspaceFacadeTest { @Mock private lateinit var mockConverter: WorkspaceFacadeConverter @Mock - private lateinit var mockTransactionManager: DatabaseTransactionManager + private lateinit var mockTransactionManager: DatabaseTransactionManagerImpl @Mock private lateinit var mockUuidValidator: UuidValidator @@ -63,7 +63,7 @@ class WorkspaceFacadeTest { fun testFindById() { val workspaceId = UUID.randomUUID() val existingWorkspace = Workspace(workspaceId, "Workspace 1", "Tag", emptyList()) - val expectedWorkspaceDTO = WorkspaceDTO(workspaceId.toString(), "Workspace 1", emptyList()) + val expectedWorkspaceDTO = WorkspaceDTO(workspaceId.toString(), "Workspace 1", emptyList(), null, "Tag") setUpMockTransactionManager() setUpMockService(existingWorkspace) @@ -95,8 +95,8 @@ class WorkspaceFacadeTest { Workspace(workspace2Id, "Workspace 2", "Tag", emptyList()) ) val expectedList = listOf( - WorkspaceDTO(workspace1Id.toString(), "Workspace 1", emptyList()), - WorkspaceDTO(workspace2Id.toString(), "Workspace 2", emptyList()) + WorkspaceDTO(workspace1Id.toString(), "Workspace 1", emptyList(), null, "Tag"), + WorkspaceDTO(workspace2Id.toString(), "Workspace 2", emptyList(), null, "Tag") ) setUpMockTransactionManager()