Этап 1: единый OkHttpClient ч.2 — 401 централизован через interceptor
CI / verify (push) Has been cancelled
CI / release (push) Has been cancelled

- UnauthorizedInterceptor (401→UnauthorizedException) в NetworkFactory,
  флаг throwOnUnauthorized (по умолчанию true).
- Удалены 75 ручных проверок if(code==401) throw в 16 репозиториях.
- AuthVerifier (login-верификация) исключён: throwOnUnauthorized=false —
  401 = 'неверный пароль' со своим сообщением, не session-expired.
- Побочно: 401 теперь ловится единообразно ВЕЗДЕ (включая пути, что раньше
  забывали проверку).
- +3 MockWebServer-теста (401 бросает; 200/403 проходят) — всего 18 тестов.

Проверено: 18 тестов зелёные + assembleRelease BUILD SUCCESSFUL, APK 53 МБ.
This commit is contained in:
b-dev-mobile
2026-07-07 23:29:49 +00:00
parent c45ebd8d49
commit ab8fa70e51
22 changed files with 73 additions and 87 deletions
+1
View File
@@ -2,6 +2,7 @@
Формат: `ГГГГ-ММ-ДД | версия | изменение | контракты | риск` Формат: `ГГГГ-ММ-ДД | версия | изменение | контракты | риск`
- 2026-07-07 | v0.5.114 (122) | Этап 1 (единый OkHttpClient, ч.2 — 401 централизован): добавлен UnauthorizedInterceptor (401→UnauthorizedException) в NetworkFactory (флаг throwOnUnauthorized, по умолч. true). Удалены 75 ручных проверок `if(code==401) throw` в 16 репозиториях. Login-верификация (AuthVerifier) исключена (throwOnUnauthorized=false: 401=«неверный пароль», не session-expired). Побочно: теперь 401 ловится единообразно ВЕЗДЕ, включая пути, что раньше проверку забывали. +3 теста (MockWebServer: 401 бросает, 200/403 проходят) — всего 18. Проверено сборкой | контракты не менялись; поведение 401 сохранено (interceptor бросает то же исключение) | средний: затрагивает обработку 401 во всех репозиториях; login-путь исключён явно, покрыто тестами
- 2026-07-07 | v0.5.114 (122) | Этап 1 (единый OkHttpClient, ч.1): NetworkFactory переписан — раньше создавал НОВЫЙ OkHttpClient на КАЖДЫЙ запрос (~32 места) → TLS-handshake + новый пул/диспатчер на запрос + утечка ExecutorService. Теперь один базовый клиент (общий ConnectionPool/Dispatcher + диск-кэш 20МБ), авторизованные варианты через base.newBuilder() (шарят пул/кэш), кэшируются по кредам+таймаутам. Публичный API НЕ менялся — 32 вызова не тронуты. Init диск-кэша — F7MobileApp.onCreate. +3 unit-теста (переиспользование клиента, общий пул). 401→interceptor НЕ делал (меняет семантику исключений — отдельный шаг) | контракты не менялись | средний: затрагивает ВСЕ сетевые вызовы (переиспользование соединений); поведение запросов то же, проверено 15 тестами + assembleRelease - 2026-07-07 | v0.5.114 (122) | Этап 1 (единый OkHttpClient, ч.1): NetworkFactory переписан — раньше создавал НОВЫЙ OkHttpClient на КАЖДЫЙ запрос (~32 места) → TLS-handshake + новый пул/диспатчер на запрос + утечка ExecutorService. Теперь один базовый клиент (общий ConnectionPool/Dispatcher + диск-кэш 20МБ), авторизованные варианты через base.newBuilder() (шарят пул/кэш), кэшируются по кредам+таймаутам. Публичный API НЕ менялся — 32 вызова не тронуты. Init диск-кэша — F7MobileApp.onCreate. +3 unit-теста (переиспользование клиента, общий пул). 401→interceptor НЕ делал (меняет семантику исключений — отдельный шаг) | контракты не менялись | средний: затрагивает ВСЕ сетевые вызовы (переиспользование соединений); поведение запросов то же, проверено 15 тестами + assembleRelease
- 2026-07-07 | v0.5.114 (122) | Этап 1 (CI + GPL-публикация): добавлен Gitea Actions workflow .gitea/workflows/ci.yml (push/PR → тесты+lint+assembleDebug; тег v* → подписанный release + архив исходников в Gitea Release) — Gitea 1.26.1 поддерживает Actions. Скрипт scripts/package-source.sh (GPL corresponding source §6: архив всего дерева вкл. vendor GPL/LICENSE/NOTICE — проверен, 6.7 МБ) закрывает п.4c, работает и вручную. docs/CI.md. НЕ активно до регистрации self-hosted раннера на инфре B (нужен Gitea-админ для токена — инфра-шаг) | процесс сборки/релиза | нет: конфиг CI, кода приложения не трогает - 2026-07-07 | v0.5.114 (122) | Этап 1 (CI + GPL-публикация): добавлен Gitea Actions workflow .gitea/workflows/ci.yml (push/PR → тесты+lint+assembleDebug; тег v* → подписанный release + архив исходников в Gitea Release) — Gitea 1.26.1 поддерживает Actions. Скрипт scripts/package-source.sh (GPL corresponding source §6: архив всего дерева вкл. vendor GPL/LICENSE/NOTICE — проверен, 6.7 МБ) закрывает п.4c, работает и вручную. docs/CI.md. НЕ активно до регистрации self-hosted раннера на инфре B (нужен Gitea-админ для токена — инфра-шаг) | процесс сборки/релиза | нет: конфиг CI, кода приложения не трогает
- 2026-07-07 | v0.5.114 (122) | Этап 1 (баги данных, test-first): заведена тестовая инфраструктура (core:network testImplementation junit) + первые 12 unit-тестов. Починено: (1) ICS-парсер дат — суффикс `Z` теперь UTC (был локальным), учитывается TZID, floating→локаль; (2) parseProps сохраняет параметры свойств — несколько ATTENDEE больше НЕ схлопываются, CN/PARTSTAT/ROLE читаются (была порча участников при редактировании→PUT на сервер); (3) unescape single-pass (последовательные replace ломались на `\\n`); (4) CalDavClient.parseIcsInstant делегирует в CalendarIcs — устранён рассинхрон календарь↔задачи; (5) QR-логин: маркер-парсер вместо split('&') — пароль с `&`/`:` больше не теряется; (6) курсор пагинации почты Int→Long (обрезка Unix-времени, ломалось ~2038). Проверено: 12 тестов зелёные + assembleRelease | контракты не менялись; исправлена запись битых ATTENDEE на сервер | низкий: чинит порчу данных, покрыто тестами - 2026-07-07 | v0.5.114 (122) | Этап 1 (баги данных, test-first): заведена тестовая инфраструктура (core:network testImplementation junit) + первые 12 unit-тестов. Починено: (1) ICS-парсер дат — суффикс `Z` теперь UTC (был локальным), учитывается TZID, floating→локаль; (2) parseProps сохраняет параметры свойств — несколько ATTENDEE больше НЕ схлопываются, CN/PARTSTAT/ROLE читаются (была порча участников при редактировании→PUT на сервер); (3) unescape single-pass (последовательные replace ломались на `\\n`); (4) CalDavClient.parseIcsInstant делегирует в CalendarIcs — устранён рассинхрон календарь↔задачи; (5) QR-логин: маркер-парсер вместо split('&') — пароль с `&`/`:` больше не теряется; (6) курсор пагинации почты Int→Long (обрезка Unix-времени, ломалось ~2038). Проверено: 12 тестов зелёные + assembleRelease | контракты не менялись; исправлена запись битых ATTENDEE на сервер | низкий: чинит порчу данных, покрыто тестами
@@ -19,7 +19,6 @@ object AppMenuRepository {
val request = Request.Builder().url(url).applyOcsJson().get().build() val request = Request.Builder().url(url).applyOcsJson().get().build()
return runCatching { return runCatching {
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
return emptyList() return emptyList()
} }
@@ -18,6 +18,8 @@ object AuthVerifier {
session.username, session.username,
session.appPassword, session.appPassword,
session.trustAllCerts, session.trustAllCerts,
// login-верификация: 401 = «неверный пароль» со своим сообщением, НЕ session-expired
throwOnUnauthorized = false,
) )
val request = Request.Builder() val request = Request.Builder()
.url("${session.serverUrl.trimEnd('/')}/ocs/v2.php/cloud/user?format=json") .url("${session.serverUrl.trimEnd('/')}/ocs/v2.php/cloud/user?format=json")
@@ -22,9 +22,6 @@ object OcsUserResolver {
.applyOcsJson() .applyOcsJson()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) {
throw UnauthorizedException()
}
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("User profile HTTP ${response.code}") error("User profile HTTP ${response.code}")
} }
+1
View File
@@ -28,4 +28,5 @@ dependencies {
api 'com.squareup.okhttp3:okhttp:4.12.0' api 'com.squareup.okhttp3:okhttp:4.12.0'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0'
} }
@@ -189,7 +189,6 @@ object CalDavClient {
.put(ics.toRequestBody("text/calendar; charset=utf-8".toMediaType())) .put(ics.toRequestBody("text/calendar; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(req).execute().use { response -> client.newCall(req).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 201 && response.code != 204) { if (response.code !in 200..299 && response.code != 201 && response.code != 204) {
error("CalDAV create event HTTP ${response.code}") error("CalDAV create event HTTP ${response.code}")
} }
@@ -244,7 +243,6 @@ object CalDavClient {
.delete() .delete()
.build() .build()
client.newCall(req).execute().use { response -> client.newCall(req).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 204 && response.code != 404) { if (response.code !in 200..299 && response.code != 204 && response.code != 404) {
error("CalDAV delete event HTTP ${response.code}") error("CalDAV delete event HTTP ${response.code}")
} }
@@ -278,7 +276,6 @@ object CalDavClient {
builder.header("If-None-Match", "*") builder.header("If-None-Match", "*")
} }
client.newCall(builder.build()).execute().use { response -> client.newCall(builder.build()).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 201 && response.code != 204) { if (response.code !in 200..299 && response.code != 201 && response.code != 204) {
error("CalDAV put event HTTP ${response.code}") error("CalDAV put event HTTP ${response.code}")
} }
@@ -314,7 +311,6 @@ object CalDavClient {
.method("MKCALENDAR", body.toRequestBody("application/xml; charset=utf-8".toMediaType())) .method("MKCALENDAR", body.toRequestBody("application/xml; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(req).execute().use { response -> client.newCall(req).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 201 && response.code != 204) { if (response.code !in 200..299 && response.code != 201 && response.code != 204) {
error("CalDAV create calendar HTTP ${response.code}") error("CalDAV create calendar HTTP ${response.code}")
} }
@@ -347,7 +343,6 @@ object CalDavClient {
.method("MKCOL", body.toRequestBody("application/xml; charset=utf-8".toMediaType())) .method("MKCOL", body.toRequestBody("application/xml; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(req).execute().use { response -> client.newCall(req).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 201 && response.code != 204) { if (response.code !in 200..299 && response.code != 201 && response.code != 204) {
error("CalDAV subscribe calendar HTTP ${response.code}") error("CalDAV subscribe calendar HTTP ${response.code}")
} }
@@ -405,7 +400,6 @@ object CalDavClient {
.header("Destination", destinationHref) .header("Destination", destinationHref)
.build() .build()
client.newCall(req).execute().use { response -> client.newCall(req).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 201 && response.code != 204) { if (response.code !in 200..299 && response.code != 201 && response.code != 204) {
error("CalDAV move HTTP ${response.code}") error("CalDAV move HTTP ${response.code}")
} }
@@ -644,7 +638,6 @@ object CalDavClient {
.put(ics.toRequestBody("text/calendar; charset=utf-8".toMediaType())) .put(ics.toRequestBody("text/calendar; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(req).execute().use { response -> client.newCall(req).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 201 && response.code != 204) { if (response.code !in 200..299 && response.code != 201 && response.code != 204) {
error("CalDAV create task HTTP ${response.code}") error("CalDAV create task HTTP ${response.code}")
} }
@@ -691,7 +684,6 @@ object CalDavClient {
.put(ics.toRequestBody("text/calendar; charset=utf-8".toMediaType())) .put(ics.toRequestBody("text/calendar; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(req).execute().use { response -> client.newCall(req).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 204) { if (response.code !in 200..299 && response.code != 204) {
error("CalDAV update task HTTP ${response.code}") error("CalDAV update task HTTP ${response.code}")
} }
@@ -705,7 +697,6 @@ object CalDavClient {
.delete() .delete()
.build() .build()
client.newCall(req).execute().use { response -> client.newCall(req).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 204 && response.code != 404) { if (response.code !in 200..299 && response.code != 204 && response.code != 404) {
error("CalDAV delete task HTTP ${response.code}") error("CalDAV delete task HTTP ${response.code}")
} }
@@ -765,7 +756,6 @@ object CalDavClient {
private fun execute(client: OkHttpClient, req: Request): String { private fun execute(client: OkHttpClient, req: Request): String {
client.newCall(req).execute().use { response -> client.newCall(req).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
val code = response.code val code = response.code
if (code !in 200..299 && code != 207) { if (code !in 200..299 && code != 207) {
error("CalDAV error HTTP $code") error("CalDAV error HTTP $code")
@@ -90,7 +90,6 @@ object CardDavClient {
.put(vcard.toRequestBody("text/vcard; charset=utf-8".toMediaType())) .put(vcard.toRequestBody("text/vcard; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(req).execute().use { response -> client.newCall(req).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 201 && response.code != 204) { if (response.code !in 200..299 && response.code != 201 && response.code != 204) {
error("Не удалось создать контакт (HTTP ${response.code})") error("Не удалось создать контакт (HTTP ${response.code})")
} }
@@ -163,7 +162,6 @@ object CardDavClient {
.method("PROPFIND", body.toRequestBody("application/xml; charset=utf-8".toMediaType())) .method("PROPFIND", body.toRequestBody("application/xml; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(req).execute().use { response -> client.newCall(req).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
val code = response.code val code = response.code
if (code !in 200..299 && code != 207) { if (code !in 200..299 && code != 207) {
error("CardDAV error HTTP $code") error("CardDAV error HTTP $code")
@@ -27,9 +27,6 @@ object DavClient {
.method("MKCOL", null) .method("MKCOL", null)
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) {
throw UnauthorizedException()
}
if (response.code !in 200..299 && response.code != 201 && response.code != 204) { if (response.code !in 200..299 && response.code != 201 && response.code != 204) {
error("DAV MKCOL HTTP ${response.code}") error("DAV MKCOL HTTP ${response.code}")
} }
@@ -46,9 +43,6 @@ object DavClient {
.put(body) .put(body)
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) {
throw UnauthorizedException()
}
if (response.code !in 200..299 && response.code != 201 && response.code != 204) { if (response.code !in 200..299 && response.code != 201 && response.code != 204) {
error("DAV upload HTTP ${response.code}") error("DAV upload HTTP ${response.code}")
} }
@@ -76,9 +70,6 @@ object DavClient {
.method("PROPFIND", body.toRequestBody("application/xml; charset=utf-8".toMediaType())) .method("PROPFIND", body.toRequestBody("application/xml; charset=utf-8".toMediaType()))
.build() .build()
return client.newCall(request).execute().use { response -> return client.newCall(request).execute().use { response ->
if (response.code == 401) {
throw UnauthorizedException()
}
val code = response.code val code = response.code
if (code !in 200..299 && code != 207) { if (code !in 200..299 && code != 207) {
error("DAV error HTTP $code") error("DAV error HTTP $code")
@@ -182,7 +173,6 @@ object DavClient {
.delete() .delete()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 204) { if (response.code !in 200..299 && response.code != 204) {
error("DAV DELETE HTTP ${response.code}") error("DAV DELETE HTTP ${response.code}")
} }
@@ -197,7 +187,6 @@ object DavClient {
.header("Overwrite", "T") .header("Overwrite", "T")
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 201 && response.code != 204) { if (response.code !in 200..299 && response.code != 201 && response.code != 204) {
error("DAV MOVE HTTP ${response.code}") error("DAV MOVE HTTP ${response.code}")
} }
@@ -221,7 +210,6 @@ object DavClient {
.method("PROPPATCH", body.toRequestBody("application/xml; charset=utf-8".toMediaType())) .method("PROPPATCH", body.toRequestBody("application/xml; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code !in 200..299 && response.code != 207) { if (response.code !in 200..299 && response.code != 207) {
error("DAV PROPPATCH HTTP ${response.code}") error("DAV PROPPATCH HTTP ${response.code}")
} }
@@ -14,9 +14,10 @@ import java.util.concurrent.TimeUnit
* - авторизованные варианты — через `base.newBuilder()` (шарят пул/диспатчер/кэш); * - авторизованные варианты — через `base.newBuilder()` (шарят пул/диспатчер/кэш);
* - экземпляры кэшируются по кредам+таймаутам → переиспользуются, соединения живут. * - экземпляры кэшируются по кредам+таймаутам → переиспользуются, соединения живут.
* *
* Публичный API НЕ менялся — все вызывающие (`newAuthedClient*`) работают как прежде. * Публичный API совместим — вызывающие (`newAuthedClient*`) работают как прежде.
* NB: 401→UnauthorizedException пока проверяется в репозиториях вручную; централизация * 401→UnauthorizedException централизован через interceptor (`throwOnUnauthorized`, по умолч.
* через interceptor — отдельный шаг (меняет семантику исключений). * true) — ручные проверки в репозиториях убраны. Login-пути (AuthVerifier), где 401 = «неверный
* пароль» со своим сообщением, передают `throwOnUnauthorized = false`.
*/ */
object NetworkFactory { object NetworkFactory {
@Volatile @Volatile
@@ -38,8 +39,9 @@ object NetworkFactory {
trustAllCerts: Boolean = false, trustAllCerts: Boolean = false,
callTimeoutSeconds: Long = 30, callTimeoutSeconds: Long = 30,
readTimeoutSeconds: Long = 30, readTimeoutSeconds: Long = 30,
throwOnUnauthorized: Boolean = true,
): OkHttpClient { ): OkHttpClient {
val key = "$username|$appPassword|$trustAllCerts|$callTimeoutSeconds|$readTimeoutSeconds" val key = "$username|$appPassword|$trustAllCerts|$callTimeoutSeconds|$readTimeoutSeconds|$throwOnUnauthorized"
return clients.getOrPut(key) { return clients.getOrPut(key) {
base.newBuilder() // общий пул/диспатчер/кэш базового клиента base.newBuilder() // общий пул/диспатчер/кэш базового клиента
.callTimeout(callTimeoutSeconds, TimeUnit.SECONDS) .callTimeout(callTimeoutSeconds, TimeUnit.SECONDS)
@@ -47,6 +49,7 @@ object NetworkFactory {
.readTimeout(readTimeoutSeconds, TimeUnit.SECONDS) .readTimeout(readTimeoutSeconds, TimeUnit.SECONDS)
.applyUnsafeSslIfNeeded(trustAllCerts) .applyUnsafeSslIfNeeded(trustAllCerts)
.addInterceptor(BasicAuthInterceptor(username, appPassword)) .addInterceptor(BasicAuthInterceptor(username, appPassword))
.apply { if (throwOnUnauthorized) addInterceptor(UnauthorizedInterceptor) }
.build() .build()
} }
} }
@@ -30,7 +30,6 @@ class NotificationsRepository {
.header("Accept", "application/json") .header("Accept", "application/json")
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Уведомления HTTP ${response.code}") error("Уведомления HTTP ${response.code}")
} }
@@ -86,7 +85,6 @@ class NotificationsRepository {
.header("Accept", "application/json") .header("Accept", "application/json")
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) { if (!response.isSuccessful) {
error("Уведомления HTTP ${response.code}") error("Уведомления HTTP ${response.code}")
} }
@@ -0,0 +1,22 @@
package ru.forbion.f7cloud.core.network
import okhttp3.Interceptor
import okhttp3.Response
/**
* Единая точка обработки 401 для авторизованных запросов: бросает [UnauthorizedException]
* (session expired → logout). Заменяет ~70 ручных `if (code == 401) throw ...` по репозиториям.
*
* Подключается в [NetworkFactory] при `throwOnUnauthorized = true` (по умолчанию). Login-пути
* (проверка пароля при входе) НЕ используют этот interceptor — там 401 = «неверный пароль».
*/
object UnauthorizedInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
if (response.code == 401) {
response.close() // не течём телом — дальше по цепочке оно не читается
throw UnauthorizedException()
}
return response
}
}
@@ -0,0 +1,40 @@
package ru.forbion.f7cloud.core.network
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
/** Централизованная обработка 401: interceptor бросает UnauthorizedException вместо ручных проверок. */
class UnauthorizedInterceptorTest {
private lateinit var server: MockWebServer
@Before fun setUp() { server = MockWebServer().apply { start() } }
@After fun tearDown() { server.shutdown() }
private fun call(client: OkHttpClient) =
client.newCall(Request.Builder().url(server.url("/x")).build()).execute()
@Test(expected = UnauthorizedException::class)
fun `401 бросает UnauthorizedException`() {
server.enqueue(MockResponse().setResponseCode(401))
val client = OkHttpClient.Builder().addInterceptor(UnauthorizedInterceptor).build()
call(client)
}
@Test fun `не-401 проходит как обычно`() {
server.enqueue(MockResponse().setResponseCode(200).setBody("ok"))
val client = OkHttpClient.Builder().addInterceptor(UnauthorizedInterceptor).build()
call(client).use { assertEquals(200, it.code) }
}
@Test fun `403 не трактуется как Unauthorized`() {
server.enqueue(MockResponse().setResponseCode(403))
val client = OkHttpClient.Builder().addInterceptor(UnauthorizedInterceptor).build()
call(client).use { assertEquals(403, it.code) }
}
}
@@ -46,7 +46,6 @@ class CalendarApiClient {
.applyOcsJson() .applyOcsJson()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
val json = parseJsonObject(response.body?.string().orEmpty(), "autocomplete attendee") val json = parseJsonObject(response.body?.string().orEmpty(), "autocomplete attendee")
val data = json.optJSONArray("data") ?: json.optJSONObject("ocs")?.optJSONArray("data") ?: return emptyList() val data = json.optJSONArray("data") ?: json.optJSONObject("ocs")?.optJSONArray("data") ?: return emptyList()
return buildList { return buildList {
@@ -77,7 +76,6 @@ class CalendarApiClient {
.applyOcsJson() .applyOcsJson()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
val json = parseJsonObject(response.body?.string().orEmpty(), "autocomplete location") val json = parseJsonObject(response.body?.string().orEmpty(), "autocomplete location")
val data = json.optJSONArray("data") ?: return emptyList() val data = json.optJSONArray("data") ?: return emptyList()
return buildList { return buildList {
@@ -104,7 +102,6 @@ class CalendarApiClient {
.applyOcsJson() .applyOcsJson()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Calendar config failed HTTP ${response.code}") if (!response.isSuccessful) error("Calendar config failed HTTP ${response.code}")
} }
} }
@@ -124,7 +121,6 @@ class CalendarApiClient {
.applyOcsJson() .applyOcsJson()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
val json = parseJsonObject(response.body?.string().orEmpty(), "create talk room") val json = parseJsonObject(response.body?.string().orEmpty(), "create talk room")
val meta = json.ocsMeta() val meta = json.ocsMeta()
if (!isOcsSuccess(meta)) error("Не удалось создать комнату Talk") if (!isOcsSuccess(meta)) error("Не удалось создать комнату Talk")
@@ -79,7 +79,6 @@ class DeckRepository {
private fun getJson(client: okhttp3.OkHttpClient, url: String): Any { private fun getJson(client: okhttp3.OkHttpClient, url: String): Any {
val request = Request.Builder().url(url).build() val request = Request.Builder().url(url).build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Deck API HTTP ${response.code}") error("Deck API HTTP ${response.code}")
} }
@@ -26,7 +26,6 @@ class SupportRepository {
.applyOcsJson() .applyOcsJson()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Support config HTTP ${response.code}") error("Support config HTTP ${response.code}")
} }
@@ -266,7 +265,6 @@ class SupportRepository {
.post(payload.toRequestBody("application/json; charset=utf-8".toMediaType())) .post(payload.toRequestBody("application/json; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) { if (!response.isSuccessful) {
val body = response.body?.string().orEmpty() val body = response.body?.string().orEmpty()
val err = runCatching { val err = runCatching {
@@ -24,7 +24,6 @@ class FileDownloadRepository(private val context: Context) {
.get() .get()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Не удалось скачать файл (HTTP ${response.code})") error("Не удалось скачать файл (HTTP ${response.code})")
} }
@@ -22,7 +22,6 @@ class FilesApiRepository {
val url = "${session.serverUrl.trimEnd('/')}/index.php/apps/files/api/v1/configs" val url = "${session.serverUrl.trimEnd('/')}/index.php/apps/files/api/v1/configs"
val request = Request.Builder().url(url).get().build() val request = Request.Builder().url(url).get().build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Files config HTTP ${response.code}") error("Files config HTTP ${response.code}")
} }
@@ -48,7 +47,6 @@ class FilesApiRepository {
.put(value.toRequestBody("text/plain".toMediaType())) .put(value.toRequestBody("text/plain".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Files config save HTTP ${response.code}") if (!response.isSuccessful) error("Files config save HTTP ${response.code}")
} }
} }
@@ -58,7 +56,6 @@ class FilesApiRepository {
val url = "${session.serverUrl.trimEnd('/')}/index.php/apps/files/api/v1/stats" val url = "${session.serverUrl.trimEnd('/')}/index.php/apps/files/api/v1/stats"
val request = Request.Builder().url(url).get().build() val request = Request.Builder().url(url).get().build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
return FilesStorageStats() return FilesStorageStats()
} }
@@ -78,7 +75,6 @@ class FilesApiRepository {
"?path=$encodedPath&depth=$depth&format=json" "?path=$encodedPath&depth=$depth&format=json"
val request = Request.Builder().url(url).applyOcsJson().get().build() val request = Request.Builder().url(url).applyOcsJson().get().build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
return emptyList() return emptyList()
} }
@@ -99,7 +95,6 @@ class FilesApiRepository {
val url = "${session.serverUrl.trimEnd('/')}/index.php/apps/files/api/v1/recent/" val url = "${session.serverUrl.trimEnd('/')}/index.php/apps/files/api/v1/recent/"
val request = Request.Builder().url(url).get().build() val request = Request.Builder().url(url).get().build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Recent files HTTP ${response.code}") error("Recent files HTTP ${response.code}")
} }
@@ -131,7 +126,6 @@ class FilesApiRepository {
"?term=$encoded&format=json" "?term=$encoded&format=json"
val request = Request.Builder().url(url).applyOcsJson().get().build() val request = Request.Builder().url(url).applyOcsJson().get().build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
return emptyList() return emptyList()
} }
@@ -188,7 +182,6 @@ class FilesApiRepository {
.build() .build()
val request = Request.Builder().url(url).applyOcsJson().post(body).build() val request = Request.Builder().url(url).applyOcsJson().post(body).build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Не удалось создать ссылку (HTTP ${response.code})") error("Не удалось создать ссылку (HTTP ${response.code})")
} }
@@ -211,7 +204,6 @@ class FilesApiRepository {
"?path=$encodedPath&reshares=true&format=json" "?path=$encodedPath&reshares=true&format=json"
val request = Request.Builder().url(url).applyOcsJson().get().build() val request = Request.Builder().url(url).applyOcsJson().get().build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) return emptyList() if (!response.isSuccessful || response.body == null) return emptyList()
val root = parseJsonObject(response.body!!.string(), "общий доступ") val root = parseJsonObject(response.body!!.string(), "общий доступ")
val data = root.optJSONObject("ocs")?.opt("data") val data = root.optJSONObject("ocs")?.opt("data")
@@ -247,7 +239,6 @@ class FilesApiRepository {
"?format=json&object_type=files&object_id=$fileId&limit=50&sort=desc" "?format=json&object_type=files&object_id=$fileId&limit=50&sort=desc"
val request = Request.Builder().url(url).applyOcsJson().get().build() val request = Request.Builder().url(url).applyOcsJson().get().build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code == 204 || !response.isSuccessful || response.body == null) return emptyList() if (response.code == 204 || !response.isSuccessful || response.body == null) return emptyList()
val root = parseJsonObject(response.body!!.string(), "события") val root = parseJsonObject(response.body!!.string(), "события")
val data = root.optJSONObject("ocs")?.opt("data") val data = root.optJSONObject("ocs")?.opt("data")
@@ -29,7 +29,6 @@ class FilesTemplatesRepository {
.post(body) .post(body)
.build() .build()
return client.newCall(request).execute().use { response -> return client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
parseOcsData(response) { data -> parseOcsData(response) { data ->
CreatedFile( CreatedFile(
fileId = data.optLong("fileid"), fileId = data.optLong("fileid"),
@@ -54,7 +53,6 @@ class FilesTemplatesRepository {
.post(body) .post(body)
.build() .build()
return client.newCall(request).execute().use { response -> return client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
parseOcsData(response) { data -> parseOcsData(response) { data ->
data.optString("template_path") data.optString("template_path")
} }
@@ -74,7 +72,6 @@ class FilesTemplatesRepository {
.post(body) .post(body)
.build() .build()
return client.newCall(request).execute().use { response -> return client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
parseOcsData(response) { data -> parseOcsData(response) { data ->
data.optString("url").ifBlank { data.optString("url").ifBlank {
error("Не удалось открыть описание папки") error("Не удалось открыть описание папки")
@@ -25,7 +25,6 @@ class RichdocumentsRepository {
.post(body) .post(body)
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Richdocuments HTTP ${response.code}") error("Richdocuments HTTP ${response.code}")
} }
@@ -121,7 +121,6 @@ class MailRepository {
.put(payload.toRequestBody("application/json".toMediaType())) .put(payload.toRequestBody("application/json".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Mail API HTTP ${response.code}") error("Mail API HTTP ${response.code}")
} }
@@ -141,7 +140,6 @@ class MailRepository {
.delete() .delete()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Mail API HTTP ${response.code}") if (!response.isSuccessful) error("Mail API HTTP ${response.code}")
} }
} }
@@ -160,7 +158,6 @@ class MailRepository {
.post(payload.toRequestBody("application/json".toMediaType())) .post(payload.toRequestBody("application/json".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Mail API HTTP ${response.code}") error("Mail API HTTP ${response.code}")
} }
@@ -178,7 +175,6 @@ class MailRepository {
.delete() .delete()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Mail API HTTP ${response.code}") if (!response.isSuccessful) error("Mail API HTTP ${response.code}")
} }
} }
@@ -221,7 +217,6 @@ class MailRepository {
.patch(payload.toString().toRequestBody("application/json".toMediaType())) .patch(payload.toString().toRequestBody("application/json".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Mail API HTTP ${response.code}") error("Mail API HTTP ${response.code}")
} }
@@ -241,7 +236,6 @@ class MailRepository {
.put(payload.toRequestBody("application/json".toMediaType())) .put(payload.toRequestBody("application/json".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Mail API HTTP ${response.code}") if (!response.isSuccessful) error("Mail API HTTP ${response.code}")
} }
} }
@@ -261,7 +255,6 @@ class MailRepository {
.post("".toRequestBody("application/json".toMediaType())) .post("".toRequestBody("application/json".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Не удалось отправить (HTTP ${response.code})") if (!response.isSuccessful) error("Не удалось отправить (HTTP ${response.code})")
} }
} }
@@ -275,7 +268,6 @@ class MailRepository {
.delete() .delete()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Не удалось удалить (HTTP ${response.code})") if (!response.isSuccessful) error("Не удалось удалить (HTTP ${response.code})")
} }
} }
@@ -301,7 +293,6 @@ class MailRepository {
.put(body.toRequestBody("application/json".toMediaType())) .put(body.toRequestBody("application/json".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Mail API HTTP ${response.code}") if (!response.isSuccessful) error("Mail API HTTP ${response.code}")
} }
} }
@@ -316,7 +307,6 @@ class MailRepository {
.post("".toRequestBody("application/json".toMediaType())) .post("".toRequestBody("application/json".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Не удалось переместить (HTTP ${response.code})") if (!response.isSuccessful) error("Не удалось переместить (HTTP ${response.code})")
} }
} }
@@ -337,7 +327,6 @@ class MailRepository {
.post("".toRequestBody("application/json".toMediaType())) .post("".toRequestBody("application/json".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Не удалось отложить (HTTP ${response.code})") if (!response.isSuccessful) error("Не удалось отложить (HTTP ${response.code})")
} }
} }
@@ -358,7 +347,6 @@ class MailRepository {
} }
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Не удалось изменить метку (HTTP ${response.code})") if (!response.isSuccessful) error("Не удалось изменить метку (HTTP ${response.code})")
} }
} }
@@ -371,7 +359,6 @@ class MailRepository {
.delete() .delete()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Mail API HTTP ${response.code}") if (!response.isSuccessful) error("Mail API HTTP ${response.code}")
} }
} }
@@ -387,7 +374,6 @@ class MailRepository {
.header("OCS-APIRequest", "true") .header("OCS-APIRequest", "true")
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Вложение HTTP ${response.code}") error("Вложение HTTP ${response.code}")
} }
@@ -422,7 +408,6 @@ class MailRepository {
.post(body) .post(body)
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Не удалось загрузить вложение (HTTP ${response.code})") error("Не удалось загрузить вложение (HTTP ${response.code})")
} }
@@ -478,7 +463,6 @@ class MailRepository {
.post(payload.toString().toRequestBody("application/json".toMediaType())) .post(payload.toString().toRequestBody("application/json".toMediaType()))
.build() .build()
val messageId = client.newCall(createRequest).execute().use { response -> val messageId = client.newCall(createRequest).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Не удалось создать письмо (HTTP ${response.code})") error("Не удалось создать письмо (HTTP ${response.code})")
} }
@@ -495,7 +479,6 @@ class MailRepository {
.post("".toRequestBody("application/json".toMediaType())) .post("".toRequestBody("application/json".toMediaType()))
.build() .build()
client.newCall(sendRequest).execute().use { response -> client.newCall(sendRequest).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) { if (!response.isSuccessful) {
error("Не удалось отправить письмо (HTTP ${response.code})") error("Не удалось отправить письмо (HTTP ${response.code})")
} }
@@ -738,7 +721,6 @@ class MailRepository {
.header("Accept", "application/json") .header("Accept", "application/json")
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code == 412) error("Mail API: CSRF — обновите приложение") if (response.code == 412) error("Mail API: CSRF — обновите приложение")
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Mail API HTTP ${response.code}") error("Mail API HTTP ${response.code}")
@@ -59,7 +59,6 @@ object TalkAttachmentUploader {
.header("Accept", "application/json") .header("Accept", "application/json")
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) return DEFAULT_ATTACHMENT_FOLDER if (!response.isSuccessful || response.body == null) return DEFAULT_ATTACHMENT_FOLDER
val data = JSONObject(response.body!!.string()) val data = JSONObject(response.body!!.string())
.optJSONObject("ocs") .optJSONObject("ocs")
@@ -110,7 +109,6 @@ object TalkAttachmentUploader {
val url = davFileUrl(session.serverUrl, davUserId, remotePath.trim('/')) val url = davFileUrl(session.serverUrl, davUserId, remotePath.trim('/'))
val request = Request.Builder().url(url).head().build() val request = Request.Builder().url(url).head().build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
return response.isSuccessful return response.isSuccessful
} }
} }
@@ -136,7 +134,6 @@ object TalkAttachmentUploader {
.method("MKCOL", ByteArray(0).toRequestBody(null)) .method("MKCOL", ByteArray(0).toRequestBody(null))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
// 405 = already exists // 405 = already exists
} }
} }
@@ -156,7 +153,6 @@ object TalkAttachmentUploader {
.put(bytes.toRequestBody(mediaType)) .put(bytes.toRequestBody(mediaType))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) { if (!response.isSuccessful) {
error("Не удалось загрузить файл: HTTP ${response.code}") error("Не удалось загрузить файл: HTTP ${response.code}")
} }
@@ -190,7 +186,6 @@ object TalkAttachmentUploader {
.post(body) .post(body)
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) { if (!response.isSuccessful) {
error("Не удалось отправить вложение в чат: HTTP ${response.code}") error("Не удалось отправить вложение в чат: HTTP ${response.code}")
} }
@@ -56,7 +56,6 @@ class TalkRepository {
.post(payload.toRequestBody("application/json; charset=utf-8".toMediaType())) .post(payload.toRequestBody("application/json; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) { if (!response.isSuccessful) {
error("Talk read marker failed: HTTP ${response.code}") error("Talk read marker failed: HTTP ${response.code}")
} }
@@ -166,7 +165,6 @@ class TalkRepository {
builder.delete() builder.delete()
} }
client.newCall(builder.build()).execute().use { response -> client.newCall(builder.build()).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Talk favorite failed: HTTP ${response.code}") if (!response.isSuccessful) error("Talk favorite failed: HTTP ${response.code}")
} }
} }
@@ -181,7 +179,6 @@ class TalkRepository {
.delete() .delete()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Talk mark unread failed: HTTP ${response.code}") if (!response.isSuccessful) error("Talk mark unread failed: HTTP ${response.code}")
} }
} }
@@ -215,7 +212,6 @@ class TalkRepository {
.post(payload.toString().toRequestBody("application/json; charset=utf-8".toMediaType())) .post(payload.toString().toRequestBody("application/json; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (response.code != 201 && !response.isSuccessful) { if (response.code != 201 && !response.isSuccessful) {
error("Talk send failed: HTTP ${response.code}") error("Talk send failed: HTTP ${response.code}")
} }
@@ -270,7 +266,6 @@ class TalkRepository {
"DELETE" -> builder.delete() "DELETE" -> builder.delete()
} }
client.newCall(builder.build()).execute().use { response -> client.newCall(builder.build()).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Talk request failed: HTTP ${response.code}") if (!response.isSuccessful) error("Talk request failed: HTTP ${response.code}")
} }
} }
@@ -288,7 +283,6 @@ class TalkRepository {
.put(body) .put(body)
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Talk edit failed: HTTP ${response.code}") if (!response.isSuccessful) error("Talk edit failed: HTTP ${response.code}")
} }
} }
@@ -323,7 +317,6 @@ class TalkRepository {
.delete() .delete()
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful) error("Talk delete failed: HTTP ${response.code}") if (!response.isSuccessful) error("Talk delete failed: HTTP ${response.code}")
} }
} }
@@ -351,7 +344,6 @@ class TalkRepository {
.post(payload.toRequestBody("application/json; charset=utf-8".toMediaType())) .post(payload.toRequestBody("application/json; charset=utf-8".toMediaType()))
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Talk request failed: HTTP ${response.code}") error("Talk request failed: HTTP ${response.code}")
} }
@@ -371,7 +363,6 @@ class TalkRepository {
.header("Accept", "application/json") .header("Accept", "application/json")
.build() .build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (response.code == 401) throw UnauthorizedException()
if (!response.isSuccessful || response.body == null) { if (!response.isSuccessful || response.body == null) {
error("Talk request failed: HTTP ${response.code}") error("Talk request failed: HTTP ${response.code}")
} }