refactor: full refactoring of project
Location and contact allocated to classes KDoc added Routes allocated to separate package
This commit is contained in:
parent
591cd28eb4
commit
e6036e2f22
11 changed files with 182 additions and 69 deletions
|
@ -1,17 +0,0 @@
|
||||||
package su.coolpeople.model
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
// https://wiki.dc09.ru/doku.php?id=wiki:coolpeople:dev:backend#анкета
|
|
||||||
@Serializable
|
|
||||||
data class Profile(
|
|
||||||
val name: String,
|
|
||||||
val age: UInt,
|
|
||||||
val lat: Float,
|
|
||||||
val lon: Float,
|
|
||||||
val city: String,
|
|
||||||
val approx_loc: Boolean,
|
|
||||||
val desc: String,
|
|
||||||
val tags: Array<String>, // TODO: maybe MutableList<String>
|
|
||||||
val contacts: Array<String>, // TODO: same
|
|
||||||
)
|
|
|
@ -1,22 +0,0 @@
|
||||||
package su.coolpeople.model
|
|
||||||
|
|
||||||
object ProfileRepository {
|
|
||||||
// TODO: Meilisearch
|
|
||||||
private val profiles = hashMapOf(
|
|
||||||
0u to Profile(
|
|
||||||
"name", 20u,
|
|
||||||
60f, 49f, "city", true,
|
|
||||||
"description",
|
|
||||||
arrayOf("programming", "music"),
|
|
||||||
arrayOf("t.me/example"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun get(id: UInt): Profile? {
|
|
||||||
return profiles.get(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun delete(id: UInt): Boolean {
|
|
||||||
return profiles.remove(id) != null
|
|
||||||
}
|
|
||||||
}
|
|
23
src/main/kotlin/su/coolpeople/models/Contact.kt
Normal file
23
src/main/kotlin/su/coolpeople/models/Contact.kt
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package su.coolpeople.models
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import su.coolpeople.models.enums.ContactType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Контакт пользователя, который он может указать в своей анкете
|
||||||
|
*
|
||||||
|
* @property type соц. сеть от которой это контакт. Например "telegram"
|
||||||
|
* @property id id пользователя в соответствующей соц. сети. Может быть сериализованным числом
|
||||||
|
* @property link ссылка на социальную сеть пользователя в виде URL
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class Contact(
|
||||||
|
val type: ContactType,
|
||||||
|
val id: String,
|
||||||
|
) {
|
||||||
|
val link: String
|
||||||
|
get() = when(type) {
|
||||||
|
ContactType.TELEGRAM -> "tg://user?id=929365483"
|
||||||
|
else -> throw NotImplementedError("Contact type $type does not have link implementation")
|
||||||
|
}
|
||||||
|
}
|
37
src/main/kotlin/su/coolpeople/models/Profile.kt
Normal file
37
src/main/kotlin/su/coolpeople/models/Profile.kt
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package su.coolpeople.models
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import su.coolpeople.models.geo.ApproximateLocation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Анкета пользователя сервиса
|
||||||
|
* [wiki](https://wiki.dc09.ru/doku.php?id=wiki:coolpeople:dev:backend#%D0%B0%D0%BD%D0%BA%D0%B5%D1%82%D0%B0)
|
||||||
|
*
|
||||||
|
* @property id анутренний id пользователя
|
||||||
|
* @property name видимое другим имя пользователя
|
||||||
|
* @property age реальный возраст пользователя
|
||||||
|
* @property location местоположение пользователя. Точные коорд
|
||||||
|
* @property description описание анкеты пользователя TODO: про проверку на XSS не забываем!!
|
||||||
|
* @property tags список тегов по которым и идет матчинг анкет
|
||||||
|
* @property contacts список привязанных к анкете
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class Profile(
|
||||||
|
val id: UInt,
|
||||||
|
val name: String,
|
||||||
|
val age: Int,
|
||||||
|
val location: ApproximateLocation,
|
||||||
|
val description: String,
|
||||||
|
val tags: List<String> = listOf(),
|
||||||
|
val contacts: List<Contact> = listOf(),
|
||||||
|
) {
|
||||||
|
suspend fun addTag(tag: String): Profile {
|
||||||
|
// data-классы как и модели по умолчанию иммутабельны. Поэтому изменяем методом и создаем копию
|
||||||
|
TODO("Реализовать когда настроим базу данных. Должно обновлять запись в бд и возвращать новый экземпляр через .copy()")
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun removeTag(tag: String): Profile {
|
||||||
|
TODO("Реализовать когда настроим базу данных. Должно обновлять запись в бд и возвращать новый экземпляр через .copy()")
|
||||||
|
}
|
||||||
|
}
|
30
src/main/kotlin/su/coolpeople/models/ProfileRepository.kt
Normal file
30
src/main/kotlin/su/coolpeople/models/ProfileRepository.kt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package su.coolpeople.models
|
||||||
|
|
||||||
|
import su.coolpeople.models.enums.ContactType
|
||||||
|
import su.coolpeople.models.geo.ApproximateLocation
|
||||||
|
|
||||||
|
object ProfileRepository {
|
||||||
|
// TODO: Meilisearch
|
||||||
|
private val profiles = mutableMapOf(
|
||||||
|
0U to Profile(
|
||||||
|
0U,
|
||||||
|
"Name",
|
||||||
|
20,
|
||||||
|
ApproximateLocation.fromCity("City"),
|
||||||
|
"im super good and interesting people",
|
||||||
|
tags = listOf("coding", "music"),
|
||||||
|
contacts = listOf(
|
||||||
|
Contact(ContactType.TELEGRAM, "1234567890"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun get(id: UInt): Profile? {
|
||||||
|
return profiles[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: а точно ли нужно удалять аккаунты? */
|
||||||
|
fun delete(id: UInt): Boolean {
|
||||||
|
return profiles.remove(id) != null
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package su.coolpeople.models.enums
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class ContactType {
|
||||||
|
@SerialName("telegram") TELEGRAM
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package su.coolpeople.models.geo
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возможно, приблизительное местоположение
|
||||||
|
*
|
||||||
|
* @property accurate является ли местоположение точным
|
||||||
|
* @property location точное местоположение. Не null только если accurate == true
|
||||||
|
* @property city город, в котором находится пользователь. Не null только если accurate == false
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class ApproximateLocation (
|
||||||
|
val accurate: Boolean,
|
||||||
|
val location: Location? = null,
|
||||||
|
val city: String? = null,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun fromLocation(location: Location): ApproximateLocation {
|
||||||
|
return ApproximateLocation(true, location)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromCity(city: String): ApproximateLocation {
|
||||||
|
return ApproximateLocation(false, city = city)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/main/kotlin/su/coolpeople/models/geo/Location.kt
Normal file
13
src/main/kotlin/su/coolpeople/models/geo/Location.kt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package su.coolpeople.models.geo
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Представление точного местоположения
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class Location(
|
||||||
|
@SerialName("lat") val latitude: Float,
|
||||||
|
@SerialName("lon") val longitude: Float,
|
||||||
|
)
|
|
@ -5,37 +5,11 @@ import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
import io.ktor.server.plugins.BadRequestException
|
import io.ktor.server.plugins.BadRequestException
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import su.coolpeople.model.ProfileRepository
|
import su.coolpeople.models.ProfileRepository
|
||||||
import kotlin.text.toUInt
|
import su.coolpeople.routes.profile
|
||||||
|
|
||||||
fun Application.configureRouting() {
|
fun Application.configureRouting() {
|
||||||
routing {
|
routing {
|
||||||
route("/api/profile") {
|
profile()
|
||||||
put {
|
|
||||||
TODO()
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/{id}") {
|
|
||||||
val id = call.parameters["id"]?.toUIntOrNull() ?: throw BadRequestException("Invalid ID")
|
|
||||||
|
|
||||||
val profile = ProfileRepository.get(id)
|
|
||||||
if (profile == null) {
|
|
||||||
call.respond(HttpStatusCode.NotFound)
|
|
||||||
return@get
|
|
||||||
}
|
|
||||||
|
|
||||||
call.respond(profile)
|
|
||||||
}
|
|
||||||
|
|
||||||
delete("/{id}") {
|
|
||||||
val id = call.parameters["id"]?.toUIntOrNull() ?: throw BadRequestException("Invalid ID")
|
|
||||||
|
|
||||||
if (!ProfileRepository.delete(id)) {
|
|
||||||
call.respond(HttpStatusCode.NotFound)
|
|
||||||
} else {
|
|
||||||
call.respond(HttpStatusCode.OK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,14 @@ import io.ktor.server.application.*
|
||||||
import io.ktor.server.plugins.contentnegotiation.*
|
import io.ktor.server.plugins.contentnegotiation.*
|
||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
fun Application.configureSerialization() {
|
fun Application.configureSerialization() {
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
json()
|
val tolerantJson = Json {
|
||||||
|
ignoreUnknownKeys = true // Это проблемы с обратной совместимостью в будующем может решить
|
||||||
|
}
|
||||||
|
|
||||||
|
json(json = tolerantJson)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
33
src/main/kotlin/su/coolpeople/routes/Profile.kt
Normal file
33
src/main/kotlin/su/coolpeople/routes/Profile.kt
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package su.coolpeople.routes
|
||||||
|
|
||||||
|
import io.ktor.http.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.plugins.*
|
||||||
|
import io.ktor.server.response.*
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
import su.coolpeople.models.ProfileRepository
|
||||||
|
|
||||||
|
fun Routing.profile() {
|
||||||
|
route("/api/profile") {
|
||||||
|
put {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/{id}") {
|
||||||
|
val id = call.parameters["id"]?.toUIntOrNull() ?: throw BadRequestException("Malformed profile id")
|
||||||
|
val profile = ProfileRepository.get(id) ?: throw NotFoundException("Profile not found")
|
||||||
|
|
||||||
|
call.respond(profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete("/{id}") {
|
||||||
|
val id = call.parameters["id"]?.toUIntOrNull() ?: throw BadRequestException("Malformed profile id")
|
||||||
|
|
||||||
|
if (!ProfileRepository.delete(id)) {
|
||||||
|
call.respond(HttpStatusCode.NotFound)
|
||||||
|
} else {
|
||||||
|
call.respond(HttpStatusCode.OK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue