diff --git a/.gitignore b/.gitignore index adfa9bf..3dcedc8 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ captures !*.xcworkspace/contents.xcworkspacedata **/xcshareddata/WorkspaceSettings.xcsettings node_modules/ + +# OpenAPI Generator metadata (generated each run, not needed in version control) +.openapi-generator/ diff --git a/build.gradle.kts b/build.gradle.kts index 23edc03..8606f0b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,4 +4,6 @@ plugins { alias(libs.plugins.composeMultiplatform) apply false alias(libs.plugins.composeCompiler) apply false alias(libs.plugins.kotlinMultiplatform) apply false + alias(libs.plugins.kotlinSerialization) apply false + alias(libs.plugins.openApiGenerator) apply false } diff --git a/composeApp/.openapi-generator-ignore b/composeApp/.openapi-generator-ignore new file mode 100644 index 0000000..a66acd4 --- /dev/null +++ b/composeApp/.openapi-generator-ignore @@ -0,0 +1,19 @@ +# OpenAPI Generator ignore file +# This file is used to exclude certain files from being overwritten during code generation. +# Files listed here will not be modified by `./gradlew generateFilcApiClient`. +# See: https://openapi-generator.tech/docs/customization/#ignore-file-format + +# Gradle build files managed by this project +build.gradle.kts +settings.gradle.kts +gradle.properties +gradlew +gradlew.bat +gradle/ + +# Documentation managed by this project +README.md + +# Android resources managed by this project +src/androidMain/ +src/iosMain/ diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 682ab77..136b312 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -1,11 +1,41 @@ // import org.jetbrains.compose.desktop.application.dsl.TargetFormat import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.openapitools.generator.gradle.plugin.tasks.GenerateTask plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.androidApplication) alias(libs.plugins.composeMultiplatform) alias(libs.plugins.composeCompiler) + alias(libs.plugins.kotlinSerialization) + alias(libs.plugins.openApiGenerator) +} + +// --------------------------------------------------------------------------- +// OpenAPI client generation +// +// Run `./gradlew generateFilcApiClient` to regenerate the API client +// whenever openapi/filc-openapi.json is updated. The generated sources are +// committed to version control so the project compiles without running the +// generator first. +// --------------------------------------------------------------------------- +val generateFilcApiClient by tasks.registering(GenerateTask::class) { + generatorName.set("kotlin") + library.set("multiplatform") + inputSpec.set(rootProject.file("openapi/filc-openapi.json").absolutePath) + outputDir.set(projectDir.absolutePath) + packageName.set("hu.petrik.filcapp.api") + apiPackage.set("hu.petrik.filcapp.api.client") + modelPackage.set("hu.petrik.filcapp.api.model") + configOptions.set( + mapOf( + "dateLibrary" to "string", + "serializationLibrary" to "kotlinx_serialization", + "omitGradleWrapper" to "true", + "omitGradlePluginVersions" to "true", + ), + ) + notCompatibleWithConfigurationCache("OpenAPI Generator Gradle plugin (org.openapi.generator) does not yet support Gradle configuration cache. See https://github.com/OpenAPITools/openapi-generator/issues/13113") } compose.resources { @@ -31,6 +61,7 @@ kotlin { androidMain.dependencies { implementation(compose.preview) implementation(libs.androidx.activity.compose) + implementation(libs.ktor.client.okhttp) } commonMain.dependencies { implementation(compose.runtime) @@ -47,6 +78,17 @@ kotlin { implementation("cafe.adriel.voyager:voyager-navigator:1.0.0") implementation("cafe.adriel.voyager:voyager-tab-navigator:1.0.0") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1") + + // Ktor HTTP client (multiplatform) + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.content.negotiation) + implementation(libs.ktor.serialization.kotlinx.json) + + // JSON serialization + implementation(libs.kotlinx.serialization.json) + + // Coroutines + implementation(libs.kotlinx.coroutines.core) } commonTest.dependencies { implementation(libs.kotlin.test) } @@ -58,6 +100,9 @@ kotlin { iosX64Main.dependsOn(this) iosArm64Main.dependsOn(this) iosSimulatorArm64Main.dependsOn(this) + dependencies { + implementation(libs.ktor.client.darwin) + } } } } diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/ApiKeyAuth.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/ApiKeyAuth.kt new file mode 100644 index 0000000..e4f06b2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/ApiKeyAuth.kt @@ -0,0 +1,20 @@ +package hu.petrik.filcapp.api.auth + +class ApiKeyAuth(private val location: String, val paramName: String) : Authentication { + var apiKey: String? = null + var apiKeyPrefix: String? = null + + override fun apply(query: MutableMap>, headers: MutableMap) { + val key: String = apiKey ?: return + val prefix: String? = apiKeyPrefix + val value: String = if (prefix != null) "$prefix $key" else key + when (location) { + "query" -> query[paramName] = listOf(value) + "header" -> headers[paramName] = value + "cookie" -> { + val existing = headers["Cookie"] + headers["Cookie"] = if (existing != null) "$existing; $paramName=$value" else "$paramName=$value" + } + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/Authentication.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/Authentication.kt new file mode 100644 index 0000000..ca5adc4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/Authentication.kt @@ -0,0 +1,13 @@ +package hu.petrik.filcapp.api.auth + +interface Authentication { + + /** + * Apply authentication settings to header and query params. + * + * @param query Query parameters. + * @param headers Header parameters. + */ + fun apply(query: MutableMap>, headers: MutableMap) + +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/HttpBasicAuth.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/HttpBasicAuth.kt new file mode 100644 index 0000000..a968c9c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/HttpBasicAuth.kt @@ -0,0 +1,15 @@ +package hu.petrik.filcapp.api.auth + +import io.ktor.util.encodeBase64 + +class HttpBasicAuth : Authentication { + var username: String? = null + var password: String? = null + + override fun apply(query: MutableMap>, headers: MutableMap) { + if (username == null && password == null) return + val str = (username ?: "") + ":" + (password ?: "") + val auth = str.encodeBase64() + headers["Authorization"] = "Basic $auth" + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/HttpBearerAuth.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/HttpBearerAuth.kt new file mode 100644 index 0000000..49875e6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/HttpBearerAuth.kt @@ -0,0 +1,14 @@ +package hu.petrik.filcapp.api.auth + +class HttpBearerAuth(private val scheme: String?) : Authentication { + var bearerToken: String? = null + + override fun apply(query: MutableMap>, headers: MutableMap) { + val token: String = bearerToken ?: return + headers["Authorization"] = (if (scheme != null) upperCaseBearer(scheme)!! + " " else "") + token + } + + private fun upperCaseBearer(scheme: String): String? { + return if ("bearer".equals(scheme, ignoreCase = true)) "Bearer" else scheme + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/OAuth.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/OAuth.kt new file mode 100644 index 0000000..fa1e391 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/auth/OAuth.kt @@ -0,0 +1,10 @@ +package hu.petrik.filcapp.api.auth + +class OAuth : Authentication { + var accessToken: String? = null + + override fun apply(query: MutableMap>, headers: MutableMap) { + val token: String = accessToken ?: return + headers["Authorization"] = "Bearer $token" + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/CohortApi.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/CohortApi.kt new file mode 100644 index 0000000..b27add6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/CohortApi.kt @@ -0,0 +1,78 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.client + +import hu.petrik.filcapp.api.model.ErrorResponse +import hu.petrik.filcapp.api.model.GetCohortsForTimetable200Response + +import hu.petrik.filcapp.api.infrastructure.* +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.request.forms.formData +import io.ktor.client.engine.HttpClientEngine +import kotlinx.serialization.json.Json +import io.ktor.http.ParametersBuilder +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +open class CohortApi : ApiClient { + + constructor( + baseUrl: String = ApiClient.BASE_URL, + httpClientEngine: HttpClientEngine? = null, + httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, + jsonSerializer: Json = ApiClient.JSON_DEFAULT + ) : super(baseUrl = baseUrl, httpClientEngine = httpClientEngine, httpClientConfig = httpClientConfig, jsonBlock = jsonSerializer) + + constructor( + baseUrl: String, + httpClient: HttpClient + ): super(baseUrl = baseUrl, httpClient = httpClient) + + /** + * List all cohorts + * + * @return GetCohortsForTimetable200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getCohorts(): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/cohort", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/NewsApi.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/NewsApi.kt new file mode 100644 index 0000000..8515089 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/NewsApi.kt @@ -0,0 +1,279 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.client + +import hu.petrik.filcapp.api.model.ErrorResponse +import hu.petrik.filcapp.api.model.GetAnnouncementById200Response +import hu.petrik.filcapp.api.model.GetAnnouncements200Response +import hu.petrik.filcapp.api.model.GetBlogById200Response +import hu.petrik.filcapp.api.model.GetBlogs200Response +import hu.petrik.filcapp.api.model.GetSystemMessageById200Response +import hu.petrik.filcapp.api.model.GetSystemMessages200Response + +import hu.petrik.filcapp.api.infrastructure.* +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.request.forms.formData +import io.ktor.client.engine.HttpClientEngine +import kotlinx.serialization.json.Json +import io.ktor.http.ParametersBuilder +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +open class NewsApi : ApiClient { + + constructor( + baseUrl: String = ApiClient.BASE_URL, + httpClientEngine: HttpClientEngine? = null, + httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, + jsonSerializer: Json = ApiClient.JSON_DEFAULT + ) : super(baseUrl = baseUrl, httpClientEngine = httpClientEngine, httpClientConfig = httpClientConfig, jsonBlock = jsonSerializer) + + constructor( + baseUrl: String, + httpClient: HttpClient + ): super(baseUrl = baseUrl, httpClient = httpClient) + + /** + * Get a specific announcement + * + * @param id + * @return GetAnnouncementById200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getAnnouncementById(id: kotlin.String): HttpResponse { + + val localVariableAuthNames = listOf() + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/news/announcements/{id}".replace("{" + "id" + "}", "$id"), + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = false, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * List all announcements + * + * @return GetAnnouncements200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getAnnouncements(): HttpResponse { + + val localVariableAuthNames = listOf() + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/news/announcements", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = false, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get a blog post by ID + * + * @param id + * @return GetBlogById200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getBlogById(id: kotlin.String): HttpResponse { + + val localVariableAuthNames = listOf() + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/news/blogs/id/{id}".replace("{" + "id" + "}", "$id"), + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = false, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get a blog post by slug + * + * @param slug + * @return GetBlogById200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getBlogBySlug(slug: kotlin.String): HttpResponse { + + val localVariableAuthNames = listOf() + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/news/blogs/{slug}".replace("{" + "slug" + "}", "$slug"), + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = false, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * List published blog posts + * + * @return GetBlogs200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getBlogs(): HttpResponse { + + val localVariableAuthNames = listOf() + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/news/blogs", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = false, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get a specific system message + * + * @param id + * @return GetSystemMessageById200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getSystemMessageById(id: kotlin.String): HttpResponse { + + val localVariableAuthNames = listOf() + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/news/system-messages/{id}".replace("{" + "id" + "}", "$id"), + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = false, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * List all system messages + * + * @return GetSystemMessages200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getSystemMessages(): HttpResponse { + + val localVariableAuthNames = listOf() + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/news/system-messages", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = false, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/PingApi.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/PingApi.kt new file mode 100644 index 0000000..2e562fe --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/PingApi.kt @@ -0,0 +1,110 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.client + +import hu.petrik.filcapp.api.model.PingResponse +import hu.petrik.filcapp.api.model.UptimeResponse + +import hu.petrik.filcapp.api.infrastructure.* +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.request.forms.formData +import io.ktor.client.engine.HttpClientEngine +import kotlinx.serialization.json.Json +import io.ktor.http.ParametersBuilder +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +open class PingApi : ApiClient { + + constructor( + baseUrl: String = ApiClient.BASE_URL, + httpClientEngine: HttpClientEngine? = null, + httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, + jsonSerializer: Json = ApiClient.JSON_DEFAULT + ) : super(baseUrl = baseUrl, httpClientEngine = httpClientEngine, httpClientConfig = httpClientConfig, jsonBlock = jsonSerializer) + + constructor( + baseUrl: String, + httpClient: HttpClient + ): super(baseUrl = baseUrl, httpClient = httpClient) + + /** + * Get server uptime + * + * @return UptimeResponse + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getUptime(): HttpResponse { + + val localVariableAuthNames = listOf() + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/ping/uptime", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = false, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Health check + * + * @return PingResponse + */ + @Suppress("UNCHECKED_CAST") + open suspend fun ping(): HttpResponse { + + val localVariableAuthNames = listOf() + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/ping", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = false, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/TimetableApi.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/TimetableApi.kt new file mode 100644 index 0000000..02e9529 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/TimetableApi.kt @@ -0,0 +1,573 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.client + +import hu.petrik.filcapp.api.model.ErrorResponse +import hu.petrik.filcapp.api.model.GetAllClassrooms200Response +import hu.petrik.filcapp.api.model.GetAllTeachers200Response +import hu.petrik.filcapp.api.model.GetCohortsForTimetable200Response +import hu.petrik.filcapp.api.model.GetLatestValidTimetable200Response +import hu.petrik.filcapp.api.model.GetLessonById200Response +import hu.petrik.filcapp.api.model.GetLessonsForCohort200Response +import hu.petrik.filcapp.api.model.GetMovedLessons200Response +import hu.petrik.filcapp.api.model.GetSubstitutions200Response +import hu.petrik.filcapp.api.model.GetTimetables200Response + +import hu.petrik.filcapp.api.infrastructure.* +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.request.forms.formData +import io.ktor.client.engine.HttpClientEngine +import kotlinx.serialization.json.Json +import io.ktor.http.ParametersBuilder +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +open class TimetableApi : ApiClient { + + constructor( + baseUrl: String = ApiClient.BASE_URL, + httpClientEngine: HttpClientEngine? = null, + httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, + jsonSerializer: Json = ApiClient.JSON_DEFAULT + ) : super(baseUrl = baseUrl, httpClientEngine = httpClientEngine, httpClientConfig = httpClientConfig, jsonBlock = jsonSerializer) + + constructor( + baseUrl: String, + httpClient: HttpClient + ): super(baseUrl = baseUrl, httpClient = httpClient) + + /** + * List all classrooms + * + * @return GetAllClassrooms200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getAllClassrooms(): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/classrooms/getAll", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * List all teachers + * + * @return GetAllTeachers200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getAllTeachers(): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/teachers/getAll", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get all cohorts for a timetable + * + * @param timetableId + * @return GetCohortsForTimetable200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getCohortsForTimetable(timetableId: kotlin.String): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/cohorts/getAllForTimetable/{timetableId}".replace("{" + "timetableId" + "}", "$timetableId"), + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get the latest valid timetable + * + * @return GetLatestValidTimetable200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getLatestValidTimetable(): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/timetables/latestValid", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get a specific lesson by ID + * + * @param lessonId + * @return GetLessonById200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getLessonById(lessonId: kotlin.String): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/lessons/getForId/{lessonId}".replace("{" + "lessonId" + "}", "$lessonId"), + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get lessons for a cohort + * + * @param cohortId + * @return GetLessonsForCohort200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getLessonsForCohort(cohortId: kotlin.String): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/lessons/getForCohort/{cohortId}".replace("{" + "cohortId" + "}", "$cohortId"), + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get lessons for a classroom + * + * @param classroomId + * @return GetLessonsForCohort200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getLessonsForRoom(classroomId: kotlin.String): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/lessons/getForRoom/{classroomId}".replace("{" + "classroomId" + "}", "$classroomId"), + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get lessons for a teacher + * + * @param teacherId + * @return GetLessonsForCohort200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getLessonsForTeacher(teacherId: kotlin.String): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/lessons/getForTeacher/{teacherId}".replace("{" + "teacherId" + "}", "$teacherId"), + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * List all moved lessons + * + * @return GetMovedLessons200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getMovedLessons(): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/movedLessons", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get moved lessons for a cohort + * + * @param cohortId + * @return GetMovedLessons200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getMovedLessonsForCohort(cohortId: kotlin.String): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/movedLessons/cohort/{cohortId}".replace("{" + "cohortId" + "}", "$cohortId"), + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get relevant moved lessons for the current user + * + * @return GetMovedLessons200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getRelevantMovedLessons(): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/movedLessons/relevant", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get relevant substitutions for the current user + * + * @return GetSubstitutions200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getRelevantSubstitutions(): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/substitutions/relevant", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * List all substitutions + * + * @return GetSubstitutions200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getSubstitutions(): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/substitutions", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get relevant substitutions for a cohort + * + * @param cohortId + * @return GetSubstitutions200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getSubstitutionsForCohort(cohortId: kotlin.String): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/substitutions/cohort/{cohortId}".replace("{" + "cohortId" + "}", "$cohortId"), + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * List all timetables + * + * @return GetTimetables200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getTimetables(): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/timetables", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + + /** + * Get all valid timetables + * + * @return GetTimetables200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getValidTimetables(): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/timetable/timetables/valid", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/UsersApi.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/UsersApi.kt new file mode 100644 index 0000000..4af93b4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/client/UsersApi.kt @@ -0,0 +1,84 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.client + +import hu.petrik.filcapp.api.model.ErrorResponse +import hu.petrik.filcapp.api.model.GetUsers200Response + +import hu.petrik.filcapp.api.infrastructure.* +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.request.forms.formData +import io.ktor.client.engine.HttpClientEngine +import kotlinx.serialization.json.Json +import io.ktor.http.ParametersBuilder +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +open class UsersApi : ApiClient { + + constructor( + baseUrl: String = ApiClient.BASE_URL, + httpClientEngine: HttpClientEngine? = null, + httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, + jsonSerializer: Json = ApiClient.JSON_DEFAULT + ) : super(baseUrl = baseUrl, httpClientEngine = httpClientEngine, httpClientConfig = httpClientConfig, jsonBlock = jsonSerializer) + + constructor( + baseUrl: String, + httpClient: HttpClient + ): super(baseUrl = baseUrl, httpClient = httpClient) + + /** + * List users with optional search and pagination + * + * @param limit (optional, default to 20) + * @param offset (optional, default to 0) + * @param search (optional) + * @return GetUsers200Response + */ + @Suppress("UNCHECKED_CAST") + open suspend fun getUsers(limit: kotlin.Int? = 20, offset: kotlin.Int? = 0, search: kotlin.String? = null): HttpResponse { + + val localVariableAuthNames = listOf("sessionAuth") + + val localVariableBody = + io.ktor.client.utils.EmptyContent + + val localVariableQuery = mutableMapOf>() + limit?.apply { localVariableQuery["limit"] = listOf("$limit") } + offset?.apply { localVariableQuery["offset"] = listOf("$offset") } + search?.apply { localVariableQuery["search"] = listOf("$search") } + val localVariableHeaders = mutableMapOf() + + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/users", + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = true, + ) + + return request( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/ApiAbstractions.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/ApiAbstractions.kt new file mode 100644 index 0000000..7b2228e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/ApiAbstractions.kt @@ -0,0 +1,23 @@ +package hu.petrik.filcapp.api.infrastructure + +typealias MultiValueMap = MutableMap> + +fun collectionDelimiter(collectionFormat: String): String = when(collectionFormat) { + "csv" -> "," + "tsv" -> "\t" + "pipe" -> "|" + "space" -> " " + else -> "" +} + +val defaultMultiValueConverter: (item: Any?) -> String = { item -> "$item" } + +fun toMultiValue(items: Array, collectionFormat: String, map: (item: T) -> String = defaultMultiValueConverter): List + = toMultiValue(items.asIterable(), collectionFormat, map) + +fun toMultiValue(items: Iterable, collectionFormat: String, map: (item: T) -> String = defaultMultiValueConverter): List { + return when(collectionFormat) { + "multi" -> items.map(map) + else -> listOf(items.joinToString(separator = collectionDelimiter(collectionFormat), transform = map)) + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/ApiClient.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/ApiClient.kt new file mode 100644 index 0000000..6dd1e40 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/ApiClient.kt @@ -0,0 +1,191 @@ +package hu.petrik.filcapp.api.infrastructure + +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.request.* +import io.ktor.client.request.forms.FormDataContent +import io.ktor.client.request.forms.MultiPartFormDataContent +import io.ktor.client.request.header +import io.ktor.client.request.parameter +import io.ktor.client.statement.HttpResponse +import io.ktor.http.ContentType +import io.ktor.serialization.kotlinx.json.json +import io.ktor.http.* +import io.ktor.http.content.PartData +import io.ktor.http.contentType +import kotlin.Unit +import kotlinx.serialization.json.Json + +import hu.petrik.filcapp.api.auth.* + +open class ApiClient( + private val baseUrl: String +) { + + private lateinit var client: HttpClient + + constructor( + baseUrl: String, + httpClientEngine: HttpClientEngine?, + httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, + jsonBlock: Json, + ) : this(baseUrl = baseUrl) { + val clientConfig: (HttpClientConfig<*>) -> Unit by lazy { + { + it.install(ContentNegotiation) { json(jsonBlock) } + httpClientConfig?.invoke(it) + } + } + + client = httpClientEngine?.let { HttpClient(it, clientConfig) } ?: HttpClient(clientConfig) + } + + constructor( + baseUrl: String, + httpClient: HttpClient + ): this(baseUrl = baseUrl) { + this.client = httpClient + } + + private val authentications: kotlin.collections.Map by lazy { + mapOf( + "sessionAuth" to ApiKeyAuth("cookie", "better-auth.session_token")) + } + + companion object { + const val BASE_URL: String = "https://filc.space/api" + val JSON_DEFAULT: Json = Json { + ignoreUnknownKeys = true + prettyPrint = true + isLenient = true + } + protected val UNSAFE_HEADERS: List = listOf(HttpHeaders.ContentType) + } + + /** + * Set the username for the first HTTP basic authentication. + * + * @param username Username + */ + fun setUsername(username: String) { + val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth? + ?: throw Exception("No HTTP basic authentication configured") + auth.username = username + } + + /** + * Set the password for the first HTTP basic authentication. + * + * @param password Password + */ + fun setPassword(password: String) { + val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth? + ?: throw Exception("No HTTP basic authentication configured") + auth.password = password + } + + /** + * Set the API key value for the first API key authentication. + * + * @param apiKey API key + * @param paramName The name of the API key parameter, or null or set the first key. + */ + fun setApiKey(apiKey: String, paramName: String? = null) { + val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName)} as ApiKeyAuth? + ?: throw Exception("No API key authentication configured") + auth.apiKey = apiKey + } + + /** + * Set the API key prefix for the first API key authentication. + * + * @param apiKeyPrefix API key prefix + * @param paramName The name of the API key parameter, or null or set the first key. + */ + fun setApiKeyPrefix(apiKeyPrefix: String, paramName: String? = null) { + val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName) } as ApiKeyAuth? + ?: throw Exception("No API key authentication configured") + auth.apiKeyPrefix = apiKeyPrefix + } + + /** + * Set the access token for the first OAuth2 authentication. + * + * @param accessToken Access token + */ + fun setAccessToken(accessToken: String) { + val auth = authentications?.values?.firstOrNull { it is OAuth } as OAuth? + ?: throw Exception("No OAuth2 authentication configured") + auth.accessToken = accessToken + } + + /** + * Set the access token for the first Bearer authentication. + * + * @param bearerToken The bearer token. + */ + fun setBearerToken(bearerToken: String) { + val auth = authentications?.values?.firstOrNull { it is HttpBearerAuth } as HttpBearerAuth? + ?: throw Exception("No Bearer authentication configured") + auth.bearerToken = bearerToken + } + + protected suspend fun multipartFormRequest(requestConfig: RequestConfig, body: kotlin.collections.List?, authNames: kotlin.collections.List): HttpResponse { + return request(requestConfig, MultiPartFormDataContent(body ?: listOf()), authNames) + } + + protected suspend fun urlEncodedFormRequest(requestConfig: RequestConfig, body: Parameters?, authNames: kotlin.collections.List): HttpResponse { + return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames) + } + + protected suspend fun jsonRequest(requestConfig: RequestConfig, body: Any? = null, authNames: kotlin.collections.List): HttpResponse = request(requestConfig, body, authNames) + + protected suspend fun request(requestConfig: RequestConfig, body: Any? = null, authNames: kotlin.collections.List): HttpResponse { + requestConfig.updateForAuth(authNames) + val headers = requestConfig.headers + + return client.request { + this.url { + this.takeFrom(URLBuilder(baseUrl)) + appendPath(requestConfig.path.trimStart('/').split('/')) + requestConfig.query.forEach { query -> + query.value.forEach { value -> + parameter(query.key, value) + } + } + } + this.method = requestConfig.method.httpMethod + headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) } + if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH)) { + val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) } + ?: ContentType.Application.Json) + this.contentType(contentType) + this.setBody(body) + } + } + } + + private fun RequestConfig.updateForAuth(authNames: kotlin.collections.List) { + for (authName in authNames) { + val auth = authentications?.get(authName) ?: throw Exception("Authentication undefined: $authName") + auth.apply(query, headers) + } + } + + private fun URLBuilder.appendPath(components: kotlin.collections.List): URLBuilder = apply { + encodedPath = encodedPath.trimEnd('/') + components.joinToString("/", prefix = "/") { it.encodeURLQueryComponent() } + } + + private val RequestMethod.httpMethod: HttpMethod + get() = when (this) { + RequestMethod.DELETE -> HttpMethod.Delete + RequestMethod.GET -> HttpMethod.Get + RequestMethod.HEAD -> HttpMethod.Head + RequestMethod.PATCH -> HttpMethod.Patch + RequestMethod.PUT -> HttpMethod.Put + RequestMethod.POST -> HttpMethod.Post + RequestMethod.OPTIONS -> HttpMethod.Options + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/Base64ByteArray.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/Base64ByteArray.kt new file mode 100644 index 0000000..1b6a654 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/Base64ByteArray.kt @@ -0,0 +1,29 @@ +package hu.petrik.filcapp.api.infrastructure + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +@Serializable(Base64ByteArray.Companion::class) +class Base64ByteArray(val value: ByteArray) { + companion object : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Base64ByteArray", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, value: Base64ByteArray): Unit = encoder.encodeString(value.value.encodeBase64()) + override fun deserialize(decoder: Decoder): Base64ByteArray = Base64ByteArray(decoder.decodeString().decodeBase64Bytes()) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + other as Base64ByteArray + return value.contentEquals(other.value) + } + + override fun hashCode(): Int { + return value.contentHashCode() + } + + override fun toString(): String { + return "Base64ByteArray(${hex(value)})" + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/Bytes.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/Bytes.kt new file mode 100644 index 0000000..718780e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/Bytes.kt @@ -0,0 +1,101 @@ +package hu.petrik.filcapp.api.infrastructure + +import io.ktor.utils.io.core.* +import kotlin.experimental.and + +private val digits = "0123456789abcdef".toCharArray() +private const val BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +private const val BASE64_MASK: Byte = 0x3f +private const val BASE64_PAD = '=' +private val BASE64_INVERSE_ALPHABET = IntArray(256) { BASE64_ALPHABET.indexOf(it.toChar()) } + +private fun String.toCharArray(): CharArray = CharArray(length) { get(it) } +private fun ByteArray.clearFrom(from: Int) = (from until size).forEach { this[it] = 0 } +private fun Int.toBase64(): Char = BASE64_ALPHABET[this] +private fun Byte.fromBase64(): Byte = BASE64_INVERSE_ALPHABET[toInt() and 0xff].toByte() and BASE64_MASK +internal fun ByteArray.encodeBase64(): String = buildPacket { writeFully(this@encodeBase64) }.encodeBase64() +internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { writeText(dropLastWhile { it == BASE64_PAD }) }.decodeBase64Bytes().readBytes() + +/** + * Encode [bytes] as a HEX string with no spaces, newlines and `0x` prefixes. + * + * Taken from https://github.com/ktorio/ktor/blob/master/ktor-utils/common/src/io/ktor/util/Crypto.kt + */ +internal fun hex(bytes: ByteArray): String { + val result = CharArray(bytes.size * 2) + var resultIndex = 0 + val digits = digits + + for (element in bytes) { + val b = element.toInt() and 0xff + result[resultIndex++] = digits[b shr 4] + result[resultIndex++] = digits[b and 0x0f] + } + + return result.concatToString() +} + +/** + * Decode bytes from HEX string. It should be no spaces and `0x` prefixes. + * + * Taken from https://github.com/ktorio/ktor/blob/master/ktor-utils/common/src/io/ktor/util/Crypto.kt + */ +internal fun hex(s: String): ByteArray { + val result = ByteArray(s.length / 2) + for (idx in result.indices) { + val srcIdx = idx * 2 + val high = s[srcIdx].toString().toInt(16) shl 4 + val low = s[srcIdx + 1].toString().toInt(16) + result[idx] = (high or low).toByte() + } + + return result +} + +/** + * Encode [ByteReadPacket] in base64 format. + * + * Taken from https://github.com/ktorio/ktor/blob/424d1d2cfaa3281302c60af9500f738c8c2fc846/ktor-utils/common/src/io/ktor/util/Base64.kt + */ +private fun ByteReadPacket.encodeBase64(): String = buildString { + val data = ByteArray(3) + while (remaining > 0) { + val read = readAvailable(data) + data.clearFrom(read) + + val padSize = (data.size - read) * 8 / 6 + val chunk = ((data[0].toInt() and 0xFF) shl 16) or + ((data[1].toInt() and 0xFF) shl 8) or + (data[2].toInt() and 0xFF) + + for (index in data.size downTo padSize) { + val char = (chunk shr (6 * index)) and BASE64_MASK.toInt() + append(char.toBase64()) + } + + repeat(padSize) { append(BASE64_PAD) } + } +} + +/** + * Decode [ByteReadPacket] from base64 format + * + * Taken from https://github.com/ktorio/ktor/blob/424d1d2cfaa3281302c60af9500f738c8c2fc846/ktor-utils/common/src/io/ktor/util/Base64.kt + */ +@Suppress("DEPRECATION") +private fun ByteReadPacket.decodeBase64Bytes(): Input = buildPacket { + val data = ByteArray(4) + + while (remaining > 0) { + val read = readAvailable(data) + + val chunk = data.foldIndexed(0) { index, result, current -> + result or (current.fromBase64().toInt() shl ((3 - index) * 6)) + } + + for (index in data.size - 2 downTo (data.size - read)) { + val origin = (chunk shr (8 * index)) and 0xff + writeByte(origin.toByte()) + } + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/HttpResponse.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/HttpResponse.kt new file mode 100644 index 0000000..1e598be --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/HttpResponse.kt @@ -0,0 +1,51 @@ +package hu.petrik.filcapp.api.infrastructure + +import io.ktor.http.Headers +import io.ktor.http.isSuccess +import io.ktor.util.reflect.TypeInfo +import io.ktor.util.reflect.typeInfo + +open class HttpResponse(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider) { + val status: Int = response.status.value + val success: Boolean = response.status.isSuccess() + val headers: Map> = response.headers.mapEntries() + suspend fun body(): T = provider.body(response) + suspend fun typedBody(type: TypeInfo): V = provider.typedBody(response, type) + + companion object { + private fun Headers.mapEntries(): Map> { + val result = mutableMapOf>() + entries().forEach { result[it.key] = it.value } + return result + } + } +} + +interface BodyProvider { + suspend fun body(response: io.ktor.client.statement.HttpResponse): T + suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V +} + +class TypedBodyProvider(private val type: TypeInfo) : BodyProvider { + @Suppress("UNCHECKED_CAST") + override suspend fun body(response: io.ktor.client.statement.HttpResponse): T = + response.call.body(type) as T + + @Suppress("UNCHECKED_CAST") + override suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V = + response.call.body(type) as V +} + +class MappedBodyProvider(private val provider: BodyProvider, private val block: S.() -> T) : BodyProvider { + override suspend fun body(response: io.ktor.client.statement.HttpResponse): T = + block(provider.body(response)) + + override suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V = + provider.typedBody(response, type) +} + +inline fun io.ktor.client.statement.HttpResponse.wrap(): HttpResponse = + HttpResponse(this, TypedBodyProvider(typeInfo())) + +fun HttpResponse.map(block: T.() -> V): HttpResponse = + HttpResponse(response, MappedBodyProvider(provider, block)) diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/OctetByteArray.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/OctetByteArray.kt new file mode 100644 index 0000000..5761a75 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/OctetByteArray.kt @@ -0,0 +1,29 @@ +package hu.petrik.filcapp.api.infrastructure + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +@Serializable(OctetByteArray.Companion::class) +class OctetByteArray(val value: ByteArray) { + companion object : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("OctetByteArray", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, value: OctetByteArray): Unit = encoder.encodeString(hex(value.value)) + override fun deserialize(decoder: Decoder): OctetByteArray = OctetByteArray(hex(decoder.decodeString())) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + other as OctetByteArray + return value.contentEquals(other.value) + } + + override fun hashCode(): Int { + return value.contentHashCode() + } + + override fun toString(): String { + return "OctetByteArray(${hex(value)})" + } +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/PartConfig.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/PartConfig.kt new file mode 100644 index 0000000..e2a90ff --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/PartConfig.kt @@ -0,0 +1,11 @@ +package hu.petrik.filcapp.api.infrastructure + +/** + * Defines a config object for a given part of a multi-part request. + * NOTE: Headers is a Map because rfc2616 defines + * multi-valued headers as csv-only. + */ +data class PartConfig( + val headers: MutableMap = mutableMapOf(), + val body: T? = null +) diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/RequestConfig.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/RequestConfig.kt new file mode 100644 index 0000000..c5d34d0 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/RequestConfig.kt @@ -0,0 +1,19 @@ +package hu.petrik.filcapp.api.infrastructure + +/** + * Defines a config object for a given request. + * NOTE: This object doesn't include 'body' because it + * allows for caching of the constructed object + * for many request definitions. + * NOTE: Headers is a Map because rfc2616 defines + * multi-valued headers as csv-only. + */ +data class RequestConfig( + val method: RequestMethod, + val path: String, + val headers: MutableMap = mutableMapOf(), + val params: MutableMap = mutableMapOf(), + val query: MutableMap> = mutableMapOf(), + val requiresAuthentication: Boolean, + val body: T? = null +) diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/RequestMethod.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/RequestMethod.kt new file mode 100644 index 0000000..9cff07e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/infrastructure/RequestMethod.kt @@ -0,0 +1,8 @@ +package hu.petrik.filcapp.api.infrastructure + +/** + * Provides enumerated HTTP verbs + */ +enum class RequestMethod { + GET, DELETE, HEAD, OPTIONS, PATCH, POST, PUT +} diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Announcement.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Announcement.kt new file mode 100644 index 0000000..587b37f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Announcement.kt @@ -0,0 +1,53 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param id + * @param title + * @param content + * @param createdAt + * @param updatedAt + * @param authorId + */ +@Serializable + +data class Announcement ( + + @SerialName(value = "id") @Required val id: kotlin.String, + + @SerialName(value = "title") @Required val title: kotlin.String, + + @SerialName(value = "content") @Required val content: kotlin.String, + + @SerialName(value = "createdAt") @Required val createdAt: kotlin.String, + + @SerialName(value = "updatedAt") val updatedAt: kotlin.String? = null, + + @SerialName(value = "authorId") val authorId: kotlin.String? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Blog.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Blog.kt new file mode 100644 index 0000000..2212ad0 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Blog.kt @@ -0,0 +1,62 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param id + * @param title + * @param slug + * @param content + * @param publishedAt + * @param excerpt + * @param createdAt + * @param updatedAt + * @param authorId + */ +@Serializable + +data class Blog ( + + @SerialName(value = "id") @Required val id: kotlin.String, + + @SerialName(value = "title") @Required val title: kotlin.String, + + @SerialName(value = "slug") @Required val slug: kotlin.String, + + @SerialName(value = "content") @Required val content: kotlin.String, + + @SerialName(value = "publishedAt") @Required val publishedAt: kotlin.String?, + + @SerialName(value = "excerpt") val excerpt: kotlin.String? = null, + + @SerialName(value = "createdAt") val createdAt: kotlin.String? = null, + + @SerialName(value = "updatedAt") val updatedAt: kotlin.String? = null, + + @SerialName(value = "authorId") val authorId: kotlin.String? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Classroom.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Classroom.kt new file mode 100644 index 0000000..be09d4e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Classroom.kt @@ -0,0 +1,41 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param id + * @param name + */ +@Serializable + +data class Classroom ( + + @SerialName(value = "id") @Required val id: kotlin.String, + + @SerialName(value = "name") @Required val name: kotlin.String + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Cohort.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Cohort.kt new file mode 100644 index 0000000..1a54590 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Cohort.kt @@ -0,0 +1,44 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param id + * @param name + * @param timetableId + */ +@Serializable + +data class Cohort ( + + @SerialName(value = "id") @Required val id: kotlin.String, + + @SerialName(value = "name") @Required val name: kotlin.String, + + @SerialName(value = "timetableId") val timetableId: kotlin.String? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/ErrorResponse.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/ErrorResponse.kt new file mode 100644 index 0000000..0e79a4d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/ErrorResponse.kt @@ -0,0 +1,41 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param error + */ +@Serializable + +data class ErrorResponse ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "error") @Required val error: kotlin.String + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAllClassrooms200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAllClassrooms200Response.kt new file mode 100644 index 0000000..26ca7ac --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAllClassrooms200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Classroom + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetAllClassrooms200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: kotlin.collections.List? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAllTeachers200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAllTeachers200Response.kt new file mode 100644 index 0000000..480b8d8 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAllTeachers200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Teacher + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetAllTeachers200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: kotlin.collections.List? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAnnouncementById200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAnnouncementById200Response.kt new file mode 100644 index 0000000..319d662 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAnnouncementById200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Announcement + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetAnnouncementById200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: Announcement? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAnnouncements200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAnnouncements200Response.kt new file mode 100644 index 0000000..2ed3bf6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetAnnouncements200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Announcement + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetAnnouncements200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: kotlin.collections.List? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetBlogById200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetBlogById200Response.kt new file mode 100644 index 0000000..97b7a0c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetBlogById200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Blog + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetBlogById200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: Blog? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetBlogs200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetBlogs200Response.kt new file mode 100644 index 0000000..53cc0d5 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetBlogs200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Blog + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetBlogs200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: kotlin.collections.List? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetCohortsForTimetable200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetCohortsForTimetable200Response.kt new file mode 100644 index 0000000..d07dc98 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetCohortsForTimetable200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Cohort + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetCohortsForTimetable200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: kotlin.collections.List? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetLatestValidTimetable200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetLatestValidTimetable200Response.kt new file mode 100644 index 0000000..a6f0d11 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetLatestValidTimetable200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Timetable + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetLatestValidTimetable200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: Timetable? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetLessonById200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetLessonById200Response.kt new file mode 100644 index 0000000..3c90811 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetLessonById200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Lesson + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetLessonById200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: Lesson? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetLessonsForCohort200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetLessonsForCohort200Response.kt new file mode 100644 index 0000000..3ad4bb7 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetLessonsForCohort200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Lesson + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetLessonsForCohort200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: kotlin.collections.List? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetMovedLessons200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetMovedLessons200Response.kt new file mode 100644 index 0000000..159b76a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetMovedLessons200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.MovedLesson + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetMovedLessons200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: kotlin.collections.List? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetSubstitutions200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetSubstitutions200Response.kt new file mode 100644 index 0000000..5ce62fa --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetSubstitutions200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Substitution + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetSubstitutions200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: kotlin.collections.List? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetSystemMessageById200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetSystemMessageById200Response.kt new file mode 100644 index 0000000..fc0f584 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetSystemMessageById200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.SystemMessage + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetSystemMessageById200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: SystemMessage? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetSystemMessages200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetSystemMessages200Response.kt new file mode 100644 index 0000000..650be93 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetSystemMessages200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.SystemMessage + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetSystemMessages200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: kotlin.collections.List? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetTimetables200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetTimetables200Response.kt new file mode 100644 index 0000000..8dc17ce --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetTimetables200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Timetable + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetTimetables200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: kotlin.collections.List? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetUsers200Response.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetUsers200Response.kt new file mode 100644 index 0000000..9ae2ad5 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetUsers200Response.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.GetUsers200ResponseAllOfData + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class GetUsers200Response ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") val `data`: GetUsers200ResponseAllOfData? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetUsers200ResponseAllOfData.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetUsers200ResponseAllOfData.kt new file mode 100644 index 0000000..00921b2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/GetUsers200ResponseAllOfData.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.User + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param users + * @param total + */ +@Serializable + +data class GetUsers200ResponseAllOfData ( + + @SerialName(value = "users") val users: kotlin.collections.List? = null, + + @SerialName(value = "total") val total: kotlin.Int? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Lesson.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Lesson.kt new file mode 100644 index 0000000..b63726d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Lesson.kt @@ -0,0 +1,77 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Classroom +import hu.petrik.filcapp.api.model.Cohort +import hu.petrik.filcapp.api.model.Teacher + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param id + * @param subject + * @param startTime + * @param endTime + * @param day + * @param period + * @param timetableId + * @param cohortId + * @param teacherId + * @param classroomId + * @param teacher + * @param classroom + * @param cohort + */ +@Serializable + +data class Lesson ( + + @SerialName(value = "id") @Required val id: kotlin.String, + + @SerialName(value = "subject") @Required val subject: kotlin.String, + + @SerialName(value = "startTime") @Required val startTime: kotlin.String, + + @SerialName(value = "endTime") @Required val endTime: kotlin.String, + + @SerialName(value = "day") val day: kotlin.Int? = null, + + @SerialName(value = "period") val period: kotlin.Int? = null, + + @SerialName(value = "timetableId") val timetableId: kotlin.String? = null, + + @SerialName(value = "cohortId") val cohortId: kotlin.String? = null, + + @SerialName(value = "teacherId") val teacherId: kotlin.String? = null, + + @SerialName(value = "classroomId") val classroomId: kotlin.String? = null, + + @SerialName(value = "teacher") val teacher: Teacher? = null, + + @SerialName(value = "classroom") val classroom: Classroom? = null, + + @SerialName(value = "cohort") val cohort: Cohort? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/MovedLesson.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/MovedLesson.kt new file mode 100644 index 0000000..bfe6a1c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/MovedLesson.kt @@ -0,0 +1,51 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Lesson + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param id + * @param originalDate + * @param newDate + * @param lessonId + * @param lesson + */ +@Serializable + +data class MovedLesson ( + + @SerialName(value = "id") @Required val id: kotlin.String, + + @SerialName(value = "originalDate") @Required val originalDate: kotlin.String, + + @SerialName(value = "newDate") @Required val newDate: kotlin.String, + + @SerialName(value = "lessonId") val lessonId: kotlin.String? = null, + + @SerialName(value = "lesson") val lesson: Lesson? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/PingResponse.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/PingResponse.kt new file mode 100644 index 0000000..a6d1ab3 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/PingResponse.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.PingResponseData + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class PingResponse ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") @Required val `data`: PingResponseData + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/PingResponseData.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/PingResponseData.kt new file mode 100644 index 0000000..7a38c1a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/PingResponseData.kt @@ -0,0 +1,38 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param message + */ +@Serializable + +data class PingResponseData ( + + @SerialName(value = "message") @Required val message: kotlin.String + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Substitution.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Substitution.kt new file mode 100644 index 0000000..7a2ba9d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Substitution.kt @@ -0,0 +1,82 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.Classroom +import hu.petrik.filcapp.api.model.Lesson +import hu.petrik.filcapp.api.model.Teacher + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param id + * @param date + * @param subject + * @param lessonId + * @param substituteTeacherId + * @param classroomId + * @param cohortId + * @param type + * @param lesson + * @param substituteTeacher + * @param classroom + */ +@Serializable + +data class Substitution ( + + @SerialName(value = "id") @Required val id: kotlin.String, + + @SerialName(value = "date") @Required val date: kotlin.String, + + @SerialName(value = "subject") val subject: kotlin.String? = null, + + @SerialName(value = "lessonId") val lessonId: kotlin.String? = null, + + @SerialName(value = "substituteTeacherId") val substituteTeacherId: kotlin.String? = null, + + @SerialName(value = "classroomId") val classroomId: kotlin.String? = null, + + @SerialName(value = "cohortId") val cohortId: kotlin.String? = null, + + @SerialName(value = "type") val type: Substitution.Type? = null, + + @SerialName(value = "lesson") val lesson: Lesson? = null, + + @SerialName(value = "substituteTeacher") val substituteTeacher: Teacher? = null, + + @SerialName(value = "classroom") val classroom: Classroom? = null + +) { + + /** + * + * + * Values: substitution,cancellation,roomChange + */ + @Serializable + enum class Type(val value: kotlin.String) { + @SerialName(value = "substitution") substitution("substitution"), + @SerialName(value = "cancellation") cancellation("cancellation"), + @SerialName(value = "roomChange") roomChange("roomChange"); + } + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/SuccessResponse.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/SuccessResponse.kt new file mode 100644 index 0000000..df74cb9 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/SuccessResponse.kt @@ -0,0 +1,38 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + */ +@Serializable + +data class SuccessResponse ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/SystemMessage.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/SystemMessage.kt new file mode 100644 index 0000000..35f45d1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/SystemMessage.kt @@ -0,0 +1,61 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param id + * @param message + * @param createdAt + * @param severity + * @param expiresAt + */ +@Serializable + +data class SystemMessage ( + + @SerialName(value = "id") @Required val id: kotlin.String, + + @SerialName(value = "message") @Required val message: kotlin.String, + + @SerialName(value = "createdAt") @Required val createdAt: kotlin.String, + + @SerialName(value = "severity") val severity: SystemMessage.Severity? = null, + + @SerialName(value = "expiresAt") val expiresAt: kotlin.String? = null + +) { + + /** + * + * + * Values: info,warning,error + */ + @Serializable + enum class Severity(val value: kotlin.String) { + @SerialName(value = "info") info("info"), + @SerialName(value = "warning") warning("warning"), + @SerialName(value = "error") error("error"); + } + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Teacher.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Teacher.kt new file mode 100644 index 0000000..cb3b1af --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Teacher.kt @@ -0,0 +1,44 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param id + * @param name + * @param shortName + */ +@Serializable + +data class Teacher ( + + @SerialName(value = "id") @Required val id: kotlin.String, + + @SerialName(value = "name") @Required val name: kotlin.String, + + @SerialName(value = "shortName") val shortName: kotlin.String? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Timetable.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Timetable.kt new file mode 100644 index 0000000..009c681 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/Timetable.kt @@ -0,0 +1,47 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param id + * @param validFrom + * @param validTo + * @param createdAt + */ +@Serializable + +data class Timetable ( + + @SerialName(value = "id") @Required val id: kotlin.String, + + @SerialName(value = "validFrom") @Required val validFrom: kotlin.String, + + @SerialName(value = "validTo") val validTo: kotlin.String? = null, + + @SerialName(value = "createdAt") val createdAt: kotlin.String? = null + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/UptimeResponse.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/UptimeResponse.kt new file mode 100644 index 0000000..ca0dcf1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/UptimeResponse.kt @@ -0,0 +1,42 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + +import hu.petrik.filcapp.api.model.UptimeResponseData + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param success + * @param `data` + */ +@Serializable + +data class UptimeResponse ( + + @SerialName(value = "success") @Required val success: kotlin.Boolean, + + @SerialName(value = "data") @Required val `data`: UptimeResponseData + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/UptimeResponseData.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/UptimeResponseData.kt new file mode 100644 index 0000000..27dd3d5 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/UptimeResponseData.kt @@ -0,0 +1,41 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param uptimeMs + * @param pretty + */ +@Serializable + +data class UptimeResponseData ( + + @SerialName(value = "uptime_ms") @Required val uptimeMs: kotlin.Int, + + @SerialName(value = "pretty") @Required val pretty: kotlin.String + +) { + + +} + diff --git a/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/User.kt b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/User.kt new file mode 100644 index 0000000..8905644 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/hu/petrik/filcapp/api/model/User.kt @@ -0,0 +1,56 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) + +package hu.petrik.filcapp.api.model + + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +/** + * + * + * @param id + * @param name + * @param email + * @param nickname + * @param roles + * @param cohortId + * @param createdAt + */ +@Serializable + +data class User ( + + @SerialName(value = "id") @Required val id: kotlin.String, + + @SerialName(value = "name") @Required val name: kotlin.String, + + @SerialName(value = "email") @Required val email: kotlin.String, + + @SerialName(value = "nickname") val nickname: kotlin.String? = null, + + @SerialName(value = "roles") val roles: kotlin.collections.List? = null, + + @SerialName(value = "cohortId") val cohortId: kotlin.String? = null, + + @SerialName(value = "createdAt") val createdAt: kotlin.String? = null + +) { + + +} + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a03f3b5..e014b5f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,6 +12,9 @@ androidx-testExt = "1.3.0" composeMultiplatform = "1.9.1" junit = "4.13.2" kotlin = "2.2.20" +ktor = "3.1.3" +kotlinx-serialization = "1.7.3" +kotlinx-coroutines = "1.9.0" [libraries] kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } @@ -24,10 +27,19 @@ androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "a androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } androidx-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" } androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } +ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } +ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } +ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } +ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } +ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" } +kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } androidLibrary = { id = "com.android.library", version.ref = "agp" } composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" } composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } -kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } \ No newline at end of file +kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +openApiGenerator = { id = "org.openapi.generator", version = "7.12.0" } \ No newline at end of file diff --git a/openapi/filc-openapi.json b/openapi/filc-openapi.json new file mode 100644 index 0000000..f07740b --- /dev/null +++ b/openapi/filc-openapi.json @@ -0,0 +1,1150 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Chronos Backend API", + "version": "0.0.1", + "description": "The Filc school management backend API" + }, + "servers": [ + { + "url": "https://filc.space/api", + "description": "Production server" + }, + { + "url": "http://localhost:3000/api", + "description": "Local development server" + } + ], + "paths": { + "/ping": { + "get": { + "operationId": "ping", + "summary": "Health check", + "tags": ["ping"], + "responses": { + "200": { + "description": "Server is alive", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PingResponse" + } + } + } + } + } + } + }, + "/ping/uptime": { + "get": { + "operationId": "getUptime", + "summary": "Get server uptime", + "tags": ["ping"], + "responses": { + "200": { + "description": "Server uptime information", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UptimeResponse" + } + } + } + } + } + } + }, + "/timetable/timetables": { + "get": { + "operationId": "getTimetables", + "summary": "List all timetables", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "responses": { + "200": { + "description": "List of timetables", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Timetable"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/timetables/latestValid": { + "get": { + "operationId": "getLatestValidTimetable", + "summary": "Get the latest valid timetable", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "responses": { + "200": { + "description": "Latest valid timetable", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": {"$ref": "#/components/schemas/Timetable"} + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/timetables/valid": { + "get": { + "operationId": "getValidTimetables", + "summary": "Get all valid timetables", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "responses": { + "200": { + "description": "All valid timetables", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Timetable"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/lessons/getForCohort/{cohortId}": { + "get": { + "operationId": "getLessonsForCohort", + "summary": "Get lessons for a cohort", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "parameters": [ + { + "name": "cohortId", + "in": "path", + "required": true, + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "Lessons for the cohort", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Lesson"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/lessons/getForTeacher/{teacherId}": { + "get": { + "operationId": "getLessonsForTeacher", + "summary": "Get lessons for a teacher", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "parameters": [ + { + "name": "teacherId", + "in": "path", + "required": true, + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "Lessons for the teacher", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Lesson"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/lessons/getForRoom/{classroomId}": { + "get": { + "operationId": "getLessonsForRoom", + "summary": "Get lessons for a classroom", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "parameters": [ + { + "name": "classroomId", + "in": "path", + "required": true, + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "Lessons for the classroom", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Lesson"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/lessons/getForId/{lessonId}": { + "get": { + "operationId": "getLessonById", + "summary": "Get a specific lesson by ID", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "parameters": [ + { + "name": "lessonId", + "in": "path", + "required": true, + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "Lesson details", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": {"$ref": "#/components/schemas/Lesson"} + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"}, + "404": {"$ref": "#/components/responses/NotFound"} + } + } + }, + "/timetable/substitutions": { + "get": { + "operationId": "getSubstitutions", + "summary": "List all substitutions", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "responses": { + "200": { + "description": "List of substitutions", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Substitution"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/substitutions/relevant": { + "get": { + "operationId": "getRelevantSubstitutions", + "summary": "Get relevant substitutions for the current user", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "responses": { + "200": { + "description": "Relevant substitutions", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Substitution"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/substitutions/cohort/{cohortId}": { + "get": { + "operationId": "getSubstitutionsForCohort", + "summary": "Get relevant substitutions for a cohort", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "parameters": [ + { + "name": "cohortId", + "in": "path", + "required": true, + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "Substitutions for the cohort", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Substitution"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/movedLessons": { + "get": { + "operationId": "getMovedLessons", + "summary": "List all moved lessons", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "responses": { + "200": { + "description": "List of moved lessons", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/MovedLesson"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/movedLessons/relevant": { + "get": { + "operationId": "getRelevantMovedLessons", + "summary": "Get relevant moved lessons for the current user", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "responses": { + "200": { + "description": "Relevant moved lessons", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/MovedLesson"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/movedLessons/cohort/{cohortId}": { + "get": { + "operationId": "getMovedLessonsForCohort", + "summary": "Get moved lessons for a cohort", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "parameters": [ + { + "name": "cohortId", + "in": "path", + "required": true, + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "Moved lessons for the cohort", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/MovedLesson"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/cohorts/getAllForTimetable/{timetableId}": { + "get": { + "operationId": "getCohortsForTimetable", + "summary": "Get all cohorts for a timetable", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "parameters": [ + { + "name": "timetableId", + "in": "path", + "required": true, + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "Cohorts for the timetable", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Cohort"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/teachers/getAll": { + "get": { + "operationId": "getAllTeachers", + "summary": "List all teachers", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "responses": { + "200": { + "description": "List of teachers", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Teacher"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/timetable/classrooms/getAll": { + "get": { + "operationId": "getAllClassrooms", + "summary": "List all classrooms", + "tags": ["timetable"], + "security": [{"sessionAuth": []}], + "responses": { + "200": { + "description": "List of classrooms", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Classroom"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/cohort": { + "get": { + "operationId": "getCohorts", + "summary": "List all cohorts", + "tags": ["cohort"], + "security": [{"sessionAuth": []}], + "responses": { + "200": { + "description": "List of cohorts", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Cohort"} + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"} + } + } + }, + "/news/announcements": { + "get": { + "operationId": "getAnnouncements", + "summary": "List all announcements", + "tags": ["news"], + "responses": { + "200": { + "description": "List of announcements", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Announcement"} + } + } + } + ] + } + } + } + } + } + } + }, + "/news/announcements/{id}": { + "get": { + "operationId": "getAnnouncementById", + "summary": "Get a specific announcement", + "tags": ["news"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "Announcement details", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": {"$ref": "#/components/schemas/Announcement"} + } + } + ] + } + } + } + }, + "404": {"$ref": "#/components/responses/NotFound"} + } + } + }, + "/news/system-messages": { + "get": { + "operationId": "getSystemMessages", + "summary": "List all system messages", + "tags": ["news"], + "responses": { + "200": { + "description": "List of system messages", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/SystemMessage"} + } + } + } + ] + } + } + } + } + } + } + }, + "/news/system-messages/{id}": { + "get": { + "operationId": "getSystemMessageById", + "summary": "Get a specific system message", + "tags": ["news"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "System message details", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": {"$ref": "#/components/schemas/SystemMessage"} + } + } + ] + } + } + } + }, + "404": {"$ref": "#/components/responses/NotFound"} + } + } + }, + "/news/blogs": { + "get": { + "operationId": "getBlogs", + "summary": "List published blog posts", + "tags": ["news"], + "responses": { + "200": { + "description": "List of published blog posts", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "array", + "items": {"$ref": "#/components/schemas/Blog"} + } + } + } + ] + } + } + } + } + } + } + }, + "/news/blogs/id/{id}": { + "get": { + "operationId": "getBlogById", + "summary": "Get a blog post by ID", + "tags": ["news"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "Blog post details", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": {"$ref": "#/components/schemas/Blog"} + } + } + ] + } + } + } + }, + "404": {"$ref": "#/components/responses/NotFound"} + } + } + }, + "/news/blogs/{slug}": { + "get": { + "operationId": "getBlogBySlug", + "summary": "Get a blog post by slug", + "tags": ["news"], + "parameters": [ + { + "name": "slug", + "in": "path", + "required": true, + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "Blog post details", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": {"$ref": "#/components/schemas/Blog"} + } + } + ] + } + } + } + }, + "404": {"$ref": "#/components/responses/NotFound"} + } + } + }, + "/users": { + "get": { + "operationId": "getUsers", + "summary": "List users with optional search and pagination", + "tags": ["users"], + "security": [{"sessionAuth": []}], + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 20} + }, + { + "name": "offset", + "in": "query", + "schema": {"type": "integer", "minimum": 0, "default": 0} + }, + { + "name": "search", + "in": "query", + "schema": {"type": "string"} + } + ], + "responses": { + "200": { + "description": "Paginated list of users", + "content": { + "application/json": { + "schema": { + "allOf": [ + {"$ref": "#/components/schemas/SuccessResponse"}, + { + "properties": { + "data": { + "type": "object", + "properties": { + "users": { + "type": "array", + "items": {"$ref": "#/components/schemas/User"} + }, + "total": {"type": "integer"} + } + } + } + } + ] + } + } + } + }, + "401": {"$ref": "#/components/responses/Unauthorized"}, + "403": {"$ref": "#/components/responses/Forbidden"} + } + } + } + }, + "components": { + "securitySchemes": { + "sessionAuth": { + "type": "apiKey", + "in": "cookie", + "name": "better-auth.session_token" + } + }, + "responses": { + "Unauthorized": { + "description": "Authentication required", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/ErrorResponse"} + } + } + }, + "Forbidden": { + "description": "Insufficient permissions", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/ErrorResponse"} + } + } + }, + "NotFound": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/ErrorResponse"} + } + } + } + }, + "schemas": { + "SuccessResponse": { + "type": "object", + "required": ["success"], + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + }, + "ErrorResponse": { + "type": "object", + "required": ["success", "error"], + "properties": { + "success": { + "type": "boolean", + "example": false + }, + "error": { + "type": "string" + } + } + }, + "PingResponse": { + "type": "object", + "required": ["success", "data"], + "properties": { + "success": {"type": "boolean"}, + "data": { + "type": "object", + "required": ["message"], + "properties": { + "message": {"type": "string", "example": "pong"} + } + } + } + }, + "UptimeResponse": { + "type": "object", + "required": ["success", "data"], + "properties": { + "success": {"type": "boolean"}, + "data": { + "type": "object", + "required": ["uptime_ms", "pretty"], + "properties": { + "uptime_ms": {"type": "integer"}, + "pretty": {"type": "string"} + } + } + } + }, + "Timetable": { + "type": "object", + "required": ["id", "validFrom"], + "properties": { + "id": {"type": "string"}, + "validFrom": {"type": "string", "format": "date"}, + "validTo": {"type": "string", "format": "date", "nullable": true}, + "createdAt": {"type": "string", "format": "date-time"} + } + }, + "Cohort": { + "type": "object", + "required": ["id", "name"], + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "timetableId": {"type": "string", "nullable": true} + } + }, + "Teacher": { + "type": "object", + "required": ["id", "name"], + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "shortName": {"type": "string", "nullable": true} + } + }, + "Classroom": { + "type": "object", + "required": ["id", "name"], + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"} + } + }, + "Lesson": { + "type": "object", + "required": ["id", "subject", "startTime", "endTime"], + "properties": { + "id": {"type": "string"}, + "subject": {"type": "string"}, + "startTime": {"type": "string"}, + "endTime": {"type": "string"}, + "day": {"type": "integer"}, + "period": {"type": "integer"}, + "timetableId": {"type": "string", "nullable": true}, + "cohortId": {"type": "string", "nullable": true}, + "teacherId": {"type": "string", "nullable": true}, + "classroomId": {"type": "string", "nullable": true}, + "teacher": {"$ref": "#/components/schemas/Teacher"}, + "classroom": {"$ref": "#/components/schemas/Classroom"}, + "cohort": {"$ref": "#/components/schemas/Cohort"} + } + }, + "Substitution": { + "type": "object", + "required": ["id", "date"], + "properties": { + "id": {"type": "string"}, + "date": {"type": "string", "format": "date"}, + "subject": {"type": "string", "nullable": true}, + "lessonId": {"type": "string", "nullable": true}, + "substituteTeacherId": {"type": "string", "nullable": true}, + "classroomId": {"type": "string", "nullable": true}, + "cohortId": {"type": "string", "nullable": true}, + "type": { + "type": "string", + "enum": ["substitution", "cancellation", "roomChange"] + }, + "lesson": {"$ref": "#/components/schemas/Lesson"}, + "substituteTeacher": {"$ref": "#/components/schemas/Teacher"}, + "classroom": {"$ref": "#/components/schemas/Classroom"} + } + }, + "MovedLesson": { + "type": "object", + "required": ["id", "originalDate", "newDate"], + "properties": { + "id": {"type": "string"}, + "originalDate": {"type": "string", "format": "date"}, + "newDate": {"type": "string", "format": "date"}, + "lessonId": {"type": "string", "nullable": true}, + "lesson": {"$ref": "#/components/schemas/Lesson"} + } + }, + "Announcement": { + "type": "object", + "required": ["id", "title", "content", "createdAt"], + "properties": { + "id": {"type": "string"}, + "title": {"type": "string"}, + "content": {"type": "string"}, + "createdAt": {"type": "string", "format": "date-time"}, + "updatedAt": {"type": "string", "format": "date-time", "nullable": true}, + "authorId": {"type": "string", "nullable": true} + } + }, + "SystemMessage": { + "type": "object", + "required": ["id", "message", "createdAt"], + "properties": { + "id": {"type": "string"}, + "message": {"type": "string"}, + "severity": { + "type": "string", + "enum": ["info", "warning", "error"] + }, + "createdAt": {"type": "string", "format": "date-time"}, + "expiresAt": {"type": "string", "format": "date-time", "nullable": true} + } + }, + "Blog": { + "type": "object", + "required": ["id", "title", "slug", "content", "publishedAt"], + "properties": { + "id": {"type": "string"}, + "title": {"type": "string"}, + "slug": {"type": "string"}, + "content": {"type": "string"}, + "excerpt": {"type": "string", "nullable": true}, + "publishedAt": {"type": "string", "format": "date-time", "nullable": true}, + "createdAt": {"type": "string", "format": "date-time"}, + "updatedAt": {"type": "string", "format": "date-time", "nullable": true}, + "authorId": {"type": "string", "nullable": true} + } + }, + "User": { + "type": "object", + "required": ["id", "name", "email"], + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "email": {"type": "string", "format": "email"}, + "nickname": {"type": "string", "nullable": true}, + "roles": { + "type": "array", + "items": {"type": "string"} + }, + "cohortId": {"type": "string", "nullable": true}, + "createdAt": {"type": "string", "format": "date-time"} + } + } + } + } +}