Skip to content

Commit a967099

Browse files
committed
feat/qg-253: WIP: добавлена обработка ошибки получения событий Google-календаря
1 parent 93825de commit a967099

File tree

29 files changed

+323
-60
lines changed

29 files changed

+323
-60
lines changed

app/src/main/kotlin/pro/azhidkov/platform/kotlin/ResultExt.kt

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package pro.azhidkov.platform.kotlin
22

3+
import kotlin.Result.Companion.failure
4+
import kotlin.Result.Companion.success
5+
36
inline fun <reified T : Throwable> Result<*>.isFailureOf(): Boolean = this.exceptionOrNull() is T
47

58
fun Result<*>.value(): Any? = if (this.isSuccess) this.getOrThrow() else this.exceptionOrNull()!!
@@ -9,7 +12,7 @@ inline fun <R : Any, reified T : Any?> Result<T>.mapSuccessOrNull(transform: (T
912

1013
@Suppress("UNCHECKED_CAST")
1114
val result = when {
12-
value != null -> Result.success(transform(value))
15+
value != null -> success(transform(value))
1316
else -> this as Result<R>
1417
}
1518
return result
@@ -20,7 +23,7 @@ inline fun <R : Any, reified T : Any> Result<T>.mapSuccess(transform: (T) -> R):
2023

2124
@Suppress("UNCHECKED_CAST")
2225
val result = when {
23-
value != null -> Result.success(transform(value))
26+
value != null -> success(transform(value))
2427
else -> this as Result<R>
2528
}
2629
return result
@@ -29,12 +32,19 @@ inline fun <R : Any, reified T : Any> Result<T>.mapSuccess(transform: (T) -> R):
2932
@Suppress("UNCHECKED_CAST")
3033
inline fun <R : Any, reified T : Any?> Result<T>.mapNull(transform: () -> R): Result<R> =
3134
when {
32-
this.isSuccess && this.getOrNull() == null -> Result.success(transform())
35+
this.isSuccess && this.getOrNull() == null -> success(transform())
3336
else -> this as Result<R>
3437
}
3538

3639
inline fun <reified T : Throwable, V : R, R> Result<V>.recoverFailure(block: (T) -> R): Result<R> =
37-
if (this.exceptionOrNull() is T) Result.success(block(this.exceptionOrNull() as T)) else this
40+
if (this.exceptionOrNull() is T) success(block(this.exceptionOrNull() as T)) else this
3841

3942
inline fun <reified T : Throwable, R> Result<R>.mapFailure(block: (T) -> Throwable): Result<R> =
40-
if (this.exceptionOrNull() is T) Result.failure(block(this.exceptionOrNull() as T)) else this
43+
if (this.exceptionOrNull() is T) failure(block(this.exceptionOrNull() as T)) else this
44+
45+
fun <T> tryExecute(eventsRequest: () -> T): Result<T> =
46+
try {
47+
success(eventsRequest())
48+
} catch (e: Exception) {
49+
failure(e)
50+
}

app/src/main/kotlin/pro/azhidkov/platform/spring/sdj/converters/CharArrayConverters.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import org.springframework.data.convert.ReadingConverter
55
import org.springframework.data.convert.WritingConverter
66

77
data class SecretChars(val value: CharArray) {
8+
9+
fun show() =
10+
String(value)
11+
812
override fun equals(other: Any?): Boolean {
913
if (this === other) return true
1014
if (javaClass != other?.javaClass) return false
@@ -17,6 +21,7 @@ data class SecretChars(val value: CharArray) {
1721
override fun hashCode(): Int {
1822
return value.contentHashCode()
1923
}
24+
2025
}
2126

2227
@WritingConverter

app/src/main/kotlin/pro/qyoga/app/therapist/appointments/core/schedule/CalendarPageModel.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ data class CalendarPageModel(
4545
val date: LocalDate,
4646
val timeMarks: List<TimeMark>,
4747
val calendarDays: Collection<CalendarDay>,
48-
val appointmentToFocus: UUID?
48+
val appointmentToFocus: UUID?,
49+
val hasSyncErrors: Boolean
4950
) : ModelAndView("therapist/appointments/schedule.html") {
5051

5152
init {
@@ -54,18 +55,19 @@ data class CalendarPageModel(
5455
addObject("calendarDays", calendarDays)
5556
addObject("selectedDayLabel", date.format(russianDayOfMonthLongFormat))
5657
addObject(FOCUSED_APPOINTMENT, appointmentToFocus)
58+
addObject("hasSyncErrors", hasSyncErrors)
5759
}
5860

5961
companion object {
6062

6163
fun of(
6264
date: LocalDate,
63-
appointments: Iterable<CalendarItem<*, LocalDateTime>>,
65+
appointments: GetCalendarAppointmentsRs,
6466
appointmentToFocus: UUID? = null
6567
): CalendarPageModel {
66-
val timeMarks = generateTimeMarks(appointments, date)
68+
val timeMarks = generateTimeMarks(appointments.appointments, date)
6769
val weekCalendar = generateDaysAround(date)
68-
return CalendarPageModel(date, timeMarks, weekCalendar, appointmentToFocus)
70+
return CalendarPageModel(date, timeMarks, weekCalendar, appointmentToFocus, appointments.hasErrors)
6971
}
7072

7173
const val FOCUSED_APPOINTMENT = "focusedAppointment"

app/src/main/kotlin/pro/qyoga/app/therapist/appointments/core/schedule/GetCalendarAppointments.kt

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package pro.qyoga.app.therapist.appointments.core.schedule
22

33
import org.springframework.stereotype.Component
44
import pro.azhidkov.platform.java.time.Interval
5+
import pro.azhidkov.platform.kotlin.tryExecute
56
import pro.qyoga.core.appointments.core.AppointmentsRepo
67
import pro.qyoga.core.calendar.api.CalendarItem
78
import pro.qyoga.core.calendar.google.GoogleCalendarsService
@@ -12,21 +13,36 @@ import pro.qyoga.core.users.therapists.TherapistRef
1213
import java.time.*
1314

1415

16+
data class GetCalendarAppointmentsRs(
17+
val appointments: List<CalendarItem<*, LocalDateTime>>,
18+
val hasErrors: Boolean
19+
)
20+
1521
@Component
1622
class GetCalendarAppointmentsOp(
1723
private val userSettingsRepo: UserSettingsRepo,
1824
private val appointmentsRepo: AppointmentsRepo,
1925
private val iCalCalendarsRepo: ICalCalendarsRepo,
2026
private val googleCalendarsService: GoogleCalendarsService
21-
) : (TherapistRef, LocalDate) -> Iterable<CalendarItem<*, LocalDateTime>> {
27+
) : (TherapistRef, LocalDate) -> GetCalendarAppointmentsRs {
2228

23-
override fun invoke(therapist: TherapistRef, date: LocalDate): Iterable<CalendarItem<*, LocalDateTime>> {
29+
override fun invoke(therapist: TherapistRef, date: LocalDate): GetCalendarAppointmentsRs {
2430
val currentUserTimeZone = userSettingsRepo.getUserTimeZone(UserRef(therapist))
2531
val interval = calendarIntervalAround(date, currentUserTimeZone)
2632
val appointments = appointmentsRepo.findCalendarItemsInInterval(therapist, interval)
27-
val drafts = iCalCalendarsRepo.findCalendarItemsInInterval(therapist, interval) +
28-
googleCalendarsService.findCalendarItemsInInterval(therapist, interval)
29-
return appointments + drafts
33+
34+
val iCalEventsResult =
35+
tryExecute { iCalCalendarsRepo.findCalendarItemsInInterval(therapist, interval) }
36+
37+
val googleCalendarEventsResult =
38+
tryExecute { googleCalendarsService.findCalendarItemsInInterval(therapist, interval) }
39+
40+
val drafts =
41+
iCalEventsResult.getOrElse { emptyList() } +
42+
googleCalendarEventsResult.getOrElse { emptyList() }
43+
44+
val hasErrors = iCalEventsResult.isFailure || googleCalendarEventsResult.isFailure
45+
return GetCalendarAppointmentsRs(appointments + drafts, hasErrors)
3046
}
3147

3248
}

app/src/main/kotlin/pro/qyoga/core/calendar/google/GoogleAccount.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ import java.util.*
1212

1313
typealias GoogleAccountRef = AggregateReference<GoogleAccount, UUID>
1414

15+
typealias GoogleAccountId = UUID
16+
1517
@Table("therapist_google_accounts")
1618
data class GoogleAccount(
1719
val ownerRef: TherapistRef,
1820
val email: String,
1921
val refreshToken: SecretChars,
2022

21-
@Id override val id: UUID = UUIDv7.randomUUID()
23+
@Id override val id: GoogleAccountId = UUIDv7.randomUUID()
2224
) : Identifiable<UUID> {
2325

2426
constructor(

app/src/main/kotlin/pro/qyoga/core/calendar/google/GoogleCalendarsClient.kt

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2Clien
1111
import org.springframework.cache.annotation.Cacheable
1212
import org.springframework.stereotype.Component
1313
import pro.azhidkov.platform.java.time.Interval
14+
import pro.azhidkov.platform.kotlin.tryExecute
1415
import pro.qyoga.core.users.therapists.TherapistRef
15-
import java.io.IOException
1616
import java.net.URI
1717
import java.time.Duration
1818
import java.time.Instant
@@ -115,11 +115,4 @@ class GoogleCalendarsClient(
115115
return service
116116
}
117117

118-
}
119-
120-
private fun <T> tryExecute(eventsRequest: () -> T): Result<T> =
121-
try {
122-
success(eventsRequest())
123-
} catch (e: IOException) {
124-
failure(e)
125-
}
118+
}

app/src/main/kotlin/pro/qyoga/core/clients/cards/model/Client.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import java.time.LocalDate
1515
import java.util.*
1616

1717
typealias ClientRef = AggregateReference<Client, UUID>
18+
typealias ClientId = UUID
1819

1920
@Table("clients")
2021
data class Client(
@@ -32,7 +33,7 @@ data class Client(
3233
val therapistRef: TherapistRef,
3334

3435
@Id
35-
override val id: UUID = UUIDv7.randomUUID(),
36+
override val id: ClientId = UUIDv7.randomUUID(),
3637
@CreatedDate
3738
val createdAt: Instant = Instant.now(),
3839
@LastModifiedDate

app/src/main/kotlin/pro/qyoga/infra/web/ThymeleafConfig.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package pro.qyoga.infra.web
33
import com.fasterxml.jackson.databind.ObjectMapper
44
import jakarta.annotation.PostConstruct
55
import org.springframework.context.annotation.Configuration
6+
import org.springframework.context.annotation.Lazy
67
import org.thymeleaf.spring6.SpringTemplateEngine
78
import org.thymeleaf.standard.StandardDialect
89
import org.thymeleaf.standard.serializer.IStandardJavaScriptSerializer
910

1011

1112
@Configuration
13+
@Lazy(false)
1214
class ThymeleafConfig(
1315
private val objectMapper: ObjectMapper,
1416
private val engine: SpringTemplateEngine

app/src/main/resources/application.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,6 @@ trainer-advisor:
106106

107107
logging:
108108
level:
109-
org.springframework.security: DEBUG
109+
org.springframework.security: WARN
110110

111111
debug: false

app/src/main/resources/templates/therapist/appointments/schedule.html

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,11 @@
9292

9393
<div class="calendar-header bg-white z-3" id="datePickerView">
9494
<div class="position-relative d-inline-block">
95-
<button class="btn btn-outline-secondary"
95+
<button
96+
class="btn btn-outline-secondary"
9697
name="datePickerButton"
97-
onclick="document.getElementById('datePicker').showPicker()"
9898
th:href="@{/therapist/schedule?calendar=true&date={date}(date=${today})}"
99+
onclick="document.getElementById('datePicker').showPicker()"
99100
>
100101
<i class="fa-solid fa-calendar"></i>
101102
</button>
@@ -115,11 +116,22 @@
115116
>
116117
</div>
117118

118-
<button class="btn btn-outline-secondary ms-2"
119+
<button
120+
class="btn btn-outline-secondary ms-2 position-relative"
119121
data-bs-target="#settingsModal"
120122
data-bs-toggle="modal"
121123
>
122124
<i class="fa-solid fa-gear"></i>
125+
<i
126+
aria-hidden="true"
127+
class="sync-error-icon
128+
fa-solid fa-circle-exclamation text-danger bg-white
129+
position-absolute top-0 start-0 translate-middle
130+
"
131+
style="border-radius: 50%;"
132+
th:if="${hasSyncErrors}"
133+
title="Ошибки синхронизации"
134+
></i>
123135
</button>
124136
</div>
125137
</div>

0 commit comments

Comments
 (0)