[API/Usos] Add basic USOS API structure.

This commit is contained in:
Kuba Szczodrzyński 2022-10-11 23:23:11 +02:00
parent 132729bbd9
commit 4b64277948
No known key found for this signature in database
GPG Key ID: 70CB8A85BA1633CB
12 changed files with 288 additions and 1 deletions

View File

@ -13,6 +13,7 @@
<w>synergia</w> <w>synergia</w>
<w>szczodrzyński</w> <w>szczodrzyński</w>
<w>szkolny</w> <w>szkolny</w>
<w>usos</w>
</words> </words>
</dictionary> </dictionary>
</component> </component>

View File

@ -204,6 +204,8 @@ const val ERROR_PODLASIE_API_NO_TOKEN = 630
const val ERROR_PODLASIE_API_OTHER = 631 const val ERROR_PODLASIE_API_OTHER = 631
const val ERROR_PODLASIE_API_DATA_MISSING = 632 const val ERROR_PODLASIE_API_DATA_MISSING = 632
const val ERROR_USOS_OAUTH_LOGIN_REQUEST = 701
const val ERROR_TEMPLATE_WEB_OTHER = 801 const val ERROR_TEMPLATE_WEB_OTHER = 801
const val EXCEPTION_API_TASK = 900 const val EXCEPTION_API_TASK = 900

View File

@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.Mobidzie
import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.login.PodlasieLoginApi import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.login.PodlasieLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginApi import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginWeb import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.login.UsosLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginHebe import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginHebe
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginWebMain import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginWebMain
import pl.szczodrzynski.edziennik.data.api.models.LoginMethod import pl.szczodrzynski.edziennik.data.api.models.LoginMethod
@ -127,6 +128,15 @@ val podlasieLoginMethods = listOf(
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED } .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
) )
const val LOGIN_TYPE_USOS = 7
const val LOGIN_MODE_USOS_OAUTH = 0
const val LOGIN_METHOD_USOS_API = 100
val usosLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_USOS, LOGIN_METHOD_USOS_API, UsosLoginApi::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
)
val templateLoginMethods = listOf( val templateLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java) LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java)
.withIsPossible { _, _ -> true } .withIsPossible { _, _ -> true }

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-10-11.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.usos
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_USOS_API
import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile
class DataUsos(
app: App,
profile: Profile?,
loginStore: LoginStore,
) : Data(app, profile, loginStore) {
fun isApiLoginValid() = oauthTokenKey != null && oauthTokenSecret != null
override fun satisfyLoginMethods() {
loginMethods.clear()
if (isApiLoginValid()) {
loginMethods += LOGIN_METHOD_USOS_API
}
}
override fun generateUserCode() = "USOS:TEST"
var oauthTokenKey: String?
get() { mOauthTokenKey = mOauthTokenKey ?: loginStore.getLoginData("oauthTokenKey", null); return mOauthTokenKey }
set(value) { loginStore.putLoginData("oauthTokenKey", value); mOauthTokenKey = value }
private var mOauthTokenKey: String? = null
var oauthTokenSecret: String?
get() { mOauthTokenSecret = mOauthTokenSecret ?: loginStore.getLoginData("oauthTokenSecret", null); return mOauthTokenSecret }
set(value) { loginStore.putLoginData("oauthTokenSecret", value); mOauthTokenSecret = value }
private var mOauthTokenSecret: String? = null
}

View File

@ -0,0 +1,109 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-10-11.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.usos
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.login.UsosLogin
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.prepare
import pl.szczodrzynski.edziennik.data.api.usosLoginMethods
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d
class Usos(
val app: App,
val profile: Profile?,
val loginStore: LoginStore,
val callback: EdziennikCallback,
) : EdziennikInterface {
companion object {
private const val TAG = "Usos"
}
val internalErrorList = mutableListOf<Int>()
val data: DataUsos
init {
data = DataUsos(app, profile, loginStore).apply {
callback = wrapCallback(this@Usos.callback)
satisfyLoginMethods()
}
}
private fun completed() {
data.saveData()
callback.onCompleted()
}
override fun sync(
featureIds: List<Int>,
viewId: Int?,
onlyEndpoints: List<Int>?,
arguments: JsonObject?,
) {
data.arguments = arguments
data.prepare(usosLoginMethods, UsosFeatures, featureIds, viewId, onlyEndpoints)
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
UsosLogin(data) {
/*UsosData(data) {
completed()
}*/
}
}
override fun getMessage(message: MessageFull) {}
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {}
override fun markAllAnnouncementsAsRead() {}
override fun getAnnouncement(announcement: AnnouncementFull) {}
override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {}
override fun getRecipientList() {}
override fun getEvent(eventFull: EventFull) {}
override fun firstLogin() {
/*UsosFirstLogin(data) {
completed()
}*/
}
override fun cancel() {
d(TAG, "Cancelled")
data.cancel()
}
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
return object : EdziennikCallback {
override fun onCompleted() {
callback.onCompleted()
}
override fun onProgress(step: Float) {
callback.onProgress(step)
}
override fun onStartProgress(stringRes: Int) {
callback.onStartProgress(stringRes)
}
override fun onError(apiError: ApiError) {
when (apiError.errorCode) {
in internalErrorList -> {
// finish immediately if the same error occurs twice during the same sync
callback.onError(apiError)
}
else -> callback.onError(apiError)
}
}
}
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-10-11.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.usos
import pl.szczodrzynski.edziennik.data.api.FEATURE_STUDENT_INFO
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_USOS_API
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_USOS
import pl.szczodrzynski.edziennik.data.api.models.Feature
const val ENDPOINT_USOS_API_USER = 7000
val UsosFeatures = listOf(
Feature(LOGIN_TYPE_USOS, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_USOS_API_USER to LOGIN_METHOD_USOS_API,
), listOf(LOGIN_METHOD_USOS_API)),
)

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-10-11.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.usos.login
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_USOS_API
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.DataUsos
import pl.szczodrzynski.edziennik.utils.Utils.d
class UsosLogin(val data: DataUsos, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "UsosLogin"
}
private var cancelled = false
init {
nextLoginMethod(onSuccess)
}
private fun nextLoginMethod(onSuccess: () -> Unit) {
if (data.targetLoginMethodIds.isEmpty()) {
onSuccess()
return
}
if (cancelled) {
onSuccess()
return
}
useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId ->
data.progress(data.progressStep)
if (usedMethodId != -1)
data.loginMethods.add(usedMethodId)
nextLoginMethod(onSuccess)
}
}
private fun useLoginMethod(loginMethodId: Int, onSuccess: (usedMethodId: Int) -> Unit) {
// this should never be true
if (data.loginMethods.contains(loginMethodId)) {
onSuccess(-1)
return
}
d(TAG, "Using login method $loginMethodId")
when (loginMethodId) {
LOGIN_METHOD_USOS_API -> {
data.startProgress(R.string.edziennik_progress_login_usos_api)
UsosLoginApi(data) { onSuccess(loginMethodId) }
}
}
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-10-11.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.usos.login
import pl.szczodrzynski.edziennik.data.api.ERROR_PROFILE_MISSING
import pl.szczodrzynski.edziennik.data.api.ERROR_USOS_OAUTH_LOGIN_REQUEST
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.DataUsos
import pl.szczodrzynski.edziennik.data.api.models.ApiError
class UsosLoginApi(val data: DataUsos, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "UsosLoginApi"
}
init { run {
if (data.profile == null) {
data.error(ApiError(TAG, ERROR_PROFILE_MISSING))
return@run
}
if (data.isApiLoginValid()) {
onSuccess()
} else {
data.error(ApiError(TAG, ERROR_USOS_OAUTH_LOGIN_REQUEST))
}
}}
}

View File

@ -313,7 +313,24 @@ object LoginInfo {
errorCodes = mapOf() errorCodes = mapOf()
) )
) )
) ),
Register(
loginType = LOGIN_TYPE_USOS,
internalName = "usos",
registerName = R.string.login_type_usos,
registerLogo = R.drawable.login_logo_usos,
loginModes = listOf(
Mode(
loginMode = LOGIN_MODE_USOS_OAUTH,
name = R.string.login_mode_usos_oauth,
icon = R.drawable.login_logo_usos,
guideText = R.string.login_mode_usos_oauth_guide,
isPlatformSelection = true,
credentials = listOf(),
errorCodes = mapOf(),
),
),
),
) )
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -174,6 +174,8 @@
<string name="error_631" translatable="false">ERROR_PODLASIE_API_OTHER</string> <string name="error_631" translatable="false">ERROR_PODLASIE_API_OTHER</string>
<string name="error_632" translatable="false">ERROR_PODLASIE_API_DATA_MISSING</string> <string name="error_632" translatable="false">ERROR_PODLASIE_API_DATA_MISSING</string>
<string name="error_701" translatable="false">ERROR_USOS_OAUTH_LOGIN_REQUEST</string>
<string name="error_801" translatable="false">ERROR_TEMPLATE_WEB_OTHER</string> <string name="error_801" translatable="false">ERROR_TEMPLATE_WEB_OTHER</string>
<string name="error_900" translatable="false">EXCEPTION_API_TASK</string> <string name="error_900" translatable="false">EXCEPTION_API_TASK</string>
@ -364,6 +366,8 @@
<string name="error_631_reason">ERROR_PODLASIE_API_OTHER</string> <string name="error_631_reason">ERROR_PODLASIE_API_OTHER</string>
<string name="error_632_reason">Brak danych. Zgłoś błąd programiście.</string> <string name="error_632_reason">Brak danych. Zgłoś błąd programiście.</string>
<string name="error_701_reason">Wymagane logowanie w przeglądarce</string>
<string name="error_801_reason">ERROR_TEMPLATE_WEB_OTHER</string> <string name="error_801_reason">ERROR_TEMPLATE_WEB_OTHER</string>
<string name="error_900_reason">Błąd synchronizacji. Upewnij się, że masz połączenie z internetem, a następnie zgłoś błąd.</string> <string name="error_900_reason">Błąd synchronizacji. Upewnij się, że masz połączenie z internetem, a następnie zgłoś błąd.</string>

View File

@ -1537,4 +1537,8 @@
<string name="login_summary_account_parent">(rodzic)</string> <string name="login_summary_account_parent">(rodzic)</string>
<string name="grades_subject_unknown">- nieznany przedmiot -</string> <string name="grades_subject_unknown">- nieznany przedmiot -</string>
<string name="grades_subject_unknown_help">{cmd-information-outline} Oceny, których przedmiot nie został podany w dzienniku. Może to być na przykład taki, który nie jest prowadzony w tym roku szkolnym.</string> <string name="grades_subject_unknown_help">{cmd-information-outline} Oceny, których przedmiot nie został podany w dzienniku. Może to być na przykład taki, który nie jest prowadzony w tym roku szkolnym.</string>
<string name="edziennik_progress_login_usos_api">Logowanie do USOS API...</string>
<string name="login_type_usos">USOS</string>
<string name="login_mode_usos_oauth">Logowanie z użyciem przeglądarki</string>
<string name="login_mode_usos_oauth_guide">TODO</string>
</resources> </resources>