module 4 lesson 1
This commit is contained in:
@ -1,11 +1,13 @@
|
||||
[versions]
|
||||
kotlin = "2.1.0"
|
||||
coroutines = "1.9.0"
|
||||
datetime = "0.6.1"
|
||||
kotlinx-coroutines = "1.9.0"
|
||||
kotlinx-datetime = "0.6.1"
|
||||
kotlinx-serialization = "1.6.3"
|
||||
okhhtp = "4.12.0"
|
||||
jackson-module = "2.18.2"
|
||||
slf4j = "2.0.9"
|
||||
logback-classic = "1.4.11"
|
||||
openapi-generator = "7.3.0"
|
||||
|
||||
# BASE
|
||||
jvm-compiler = "17"
|
||||
@ -15,13 +17,18 @@ jvm-language = "21"
|
||||
jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
java-gradle-plugin = { id = "java-gradle-plugin" }
|
||||
openapi-generator = { id = "org.openapi.generator", version.ref = "openapi-generator" }
|
||||
kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
|
||||
[libraries]
|
||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
|
||||
kotlin-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
|
||||
kotlin-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
|
||||
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
|
||||
kotlin-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
|
||||
kotlin-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }
|
||||
kotlin-jackson-module = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module" }
|
||||
kotlin-jackson-datatype = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310", version.ref = "jackson-module" }
|
||||
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" }
|
||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
|
||||
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhhtp" }
|
||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback-classic" }
|
||||
|
||||
@ -4,4 +4,29 @@ plugins {
|
||||
}
|
||||
|
||||
group = "ru.otus.messenger"
|
||||
version = "0.0.1"
|
||||
version = "0.0.1"
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
group = rootProject.group
|
||||
version = rootProject.version
|
||||
}
|
||||
|
||||
ext {
|
||||
val specDir = layout.projectDirectory.dir("../specs")
|
||||
set("spec-v1", specDir.file("specs-v1.yaml").toString())
|
||||
}
|
||||
|
||||
tasks {
|
||||
arrayOf("build", "clean", "check").forEach {tsk ->
|
||||
create(tsk) {
|
||||
group = "build"
|
||||
dependsOn(subprojects.map { it.getTasksByName(tsk,false)})
|
||||
}
|
||||
}
|
||||
}
|
||||
16
ok-messenger-be/ok-messenger-api-v1-mappers/build.gradle.kts
Normal file
16
ok-messenger-be/ok-messenger-api-v1-mappers/build.gradle.kts
Normal file
@ -0,0 +1,16 @@
|
||||
plugins {
|
||||
id("build-jvm")
|
||||
}
|
||||
|
||||
group = rootProject.group
|
||||
version = rootProject.version
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(libs.kotlin.datetime)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(project(":ok-messenger-api-v1"))
|
||||
implementation(project(":ok-messenger-common"))
|
||||
|
||||
testImplementation(kotlin("test-junit"))
|
||||
}
|
||||
@ -0,0 +1,148 @@
|
||||
package ru.otus.messenger.api.v1.mappers
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import ru.otus.messenger.api.v1.mappers.exceptions.UnknownRequestClass
|
||||
import ru.otus.messenger.api.v1.models.*
|
||||
import ru.otus.messenger.common.models.*
|
||||
import ru.otus.messenger.common.ChatContext
|
||||
import ru.otus.messenger.common.stubs.Stubs
|
||||
|
||||
fun ChatContext.fromTransport(request: IRequest) = when (request) {
|
||||
is ChatCreateRequest -> fromTransport(request)
|
||||
is ChatReadRequest -> fromTransport(request)
|
||||
is ChatDeleteRequest -> fromTransport(request)
|
||||
is ChatSearchRequest -> fromTransport(request)
|
||||
is ChatUpdateRequest -> fromTransport(request)
|
||||
else -> throw UnknownRequestClass(request.javaClass)
|
||||
}
|
||||
|
||||
private fun String?.toChatId() = this?.let { ChatId(it) } ?: ChatId.NONE
|
||||
private fun String?.toOwnerId() = this?.let { ChatOwnerId(it) } ?: ChatOwnerId.NONE
|
||||
private fun String?.toChatUserId() = this?.let { ChatUserId(it) } ?: ChatUserId.NONE
|
||||
private fun String?.toChatMetadata() = this?.let { ChatMetadata(Json.parseToJsonElement(it).jsonObject) } ?: ChatMetadata.NONE
|
||||
private fun Boolean?.toChatArchiveFlag() = this?.let { ChatArchiveFlag(it) } ?: ChatArchiveFlag.NONE
|
||||
private fun String?.toParticipants() = this?.let { mutableSetOf(it.toChatUserId()) } ?: mutableSetOf()
|
||||
|
||||
private fun Debug?.transportToWorkMode(): WorkMode = when (this?.mode) {
|
||||
DebugMode.PROD -> WorkMode.PROD
|
||||
DebugMode.TEST -> WorkMode.TEST
|
||||
DebugMode.STUB -> WorkMode.STUB
|
||||
null -> WorkMode.PROD
|
||||
}
|
||||
|
||||
private fun Debug?.transportToStubCase(): Stubs = when (this?.stub) {
|
||||
DebugStubs.SUCCESS -> Stubs.SUCCESS
|
||||
DebugStubs.NOT_FOUND -> Stubs.NOT_FOUND
|
||||
DebugStubs.VALUE_ERROR -> Stubs.VALUE_ERROR
|
||||
DebugStubs.MISSING_DATA -> Stubs.MISSING_DATA
|
||||
DebugStubs.CANNOT_DELETE -> Stubs.CANNOT_DELETE
|
||||
null -> Stubs.NONE
|
||||
}
|
||||
|
||||
fun ChatContext.fromTransport(request: ChatCreateRequest) {
|
||||
command = ChatCommand.CREATE
|
||||
chatRequest = request.chat?.toInternal() ?: MessengerChat()
|
||||
workMode = request.debug.transportToWorkMode()
|
||||
stubCase = request.debug.transportToStubCase()
|
||||
}
|
||||
|
||||
fun ChatContext.fromTransport(request: ChatReadRequest) {
|
||||
command = ChatCommand.READ
|
||||
chatRequest = request.chatId.toChatId().toInternal()
|
||||
workMode = request.debug.transportToWorkMode()
|
||||
stubCase = request.debug.transportToStubCase()
|
||||
}
|
||||
|
||||
fun ChatContext.fromTransport(request: ChatDeleteRequest) {
|
||||
command = ChatCommand.DELETE
|
||||
chatRequest = request.chatId.toChatId().toInternal()
|
||||
workMode = request.debug.transportToWorkMode()
|
||||
stubCase = request.debug.transportToStubCase()
|
||||
}
|
||||
|
||||
fun ChatContext.fromTransport(request: ChatUpdateRequest) {
|
||||
command = ChatCommand.UPDATE
|
||||
chatRequest = request.chat?.toInternal() ?: MessengerChat()
|
||||
workMode = request.debug.transportToWorkMode()
|
||||
stubCase = request.debug.transportToStubCase()
|
||||
}
|
||||
|
||||
fun ChatContext.fromTransport(request: ChatSearchRequest) {
|
||||
command = ChatCommand.SEARCH
|
||||
chatRequest = request.criteria?.toInternal() ?: MessengerChat()
|
||||
workMode = request.debug.transportToWorkMode()
|
||||
stubCase = request.debug.transportToStubCase()
|
||||
}
|
||||
|
||||
private fun ChatCreateRequestAllOfChat.toInternal(): MessengerChat = MessengerChat(
|
||||
title = this.title ?: "",
|
||||
description = this.description ?: "",
|
||||
type = this.type.fromTransport(),
|
||||
mode = this.mode.fromTransport(),
|
||||
ownerId = this.ownerId.toOwnerId(),
|
||||
participants = this.participants.fromTransport(),
|
||||
metadata = this.metadata.toChatMetadata(),
|
||||
)
|
||||
|
||||
private fun ChatUpdateRequestAllOfChat.toInternal(): MessengerChat = MessengerChat(
|
||||
id = this.chatId.toChatId(),
|
||||
title = this.title ?: "",
|
||||
description = this.description ?: "",
|
||||
mode = this.mode.fromTransport(),
|
||||
isArchived = this.isArchived.toChatArchiveFlag(),
|
||||
metadata = this.metadata.toChatMetadata(),
|
||||
)
|
||||
|
||||
private fun ChatSearchRequestAllOfCriteria.toInternal(): MessengerChat = MessengerChat(
|
||||
title = this.title ?: "",
|
||||
type = this.type.fromTransport(),
|
||||
mode = this.mode.fromTransport(),
|
||||
participants = this.participant.toParticipants(),
|
||||
)
|
||||
|
||||
private fun ChatId.toInternal(): MessengerChat = if (this.asString() != "") {
|
||||
MessengerChat(id = this)
|
||||
} else {
|
||||
MessengerChat()
|
||||
}
|
||||
|
||||
private fun ChatCreateRequestAllOfChat.Type?.fromTransport() = when (this) {
|
||||
ChatCreateRequestAllOfChat.Type.PRIVATE -> ChatType.PRIVATE
|
||||
ChatCreateRequestAllOfChat.Type.GROUP -> ChatType.GROUP
|
||||
ChatCreateRequestAllOfChat.Type.CHANNEL -> ChatType.CHANNEL
|
||||
null -> ChatType.NONE
|
||||
}
|
||||
|
||||
private fun ChatCreateRequestAllOfChat.Mode?.fromTransport() = when (this) {
|
||||
ChatCreateRequestAllOfChat.Mode.PERSONAL -> ChatMode.PERSONAL
|
||||
ChatCreateRequestAllOfChat.Mode.WORK -> ChatMode.WORK
|
||||
null -> ChatMode.NONE
|
||||
}
|
||||
|
||||
private fun ChatUpdateRequestAllOfChat.Mode?.fromTransport() = when (this) {
|
||||
ChatUpdateRequestAllOfChat.Mode.PERSONAL -> ChatMode.PERSONAL
|
||||
ChatUpdateRequestAllOfChat.Mode.WORK -> ChatMode.WORK
|
||||
null -> ChatMode.NONE
|
||||
}
|
||||
|
||||
private fun ChatSearchRequestAllOfCriteria.Type?.fromTransport() = when (this) {
|
||||
ChatSearchRequestAllOfCriteria.Type.PRIVATE -> ChatType.PRIVATE
|
||||
ChatSearchRequestAllOfCriteria.Type.GROUP -> ChatType.GROUP
|
||||
ChatSearchRequestAllOfCriteria.Type.CHANNEL -> ChatType.CHANNEL
|
||||
null -> ChatType.NONE
|
||||
}
|
||||
|
||||
private fun ChatSearchRequestAllOfCriteria.Mode?.fromTransport() = when (this) {
|
||||
ChatSearchRequestAllOfCriteria.Mode.PERSONAL -> ChatMode.PERSONAL
|
||||
ChatSearchRequestAllOfCriteria.Mode.WORK -> ChatMode.WORK
|
||||
null -> ChatMode.NONE
|
||||
}
|
||||
|
||||
private fun Set<String>?.fromTransport(): MutableSet<ChatUserId> = if (this != null) {
|
||||
this.map { it.toChatUserId() }
|
||||
.toMutableSet()
|
||||
.takeIf { it.isNotEmpty() } ?: mutableSetOf(ChatUserId(""))
|
||||
} else {
|
||||
mutableSetOf()
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package ru.otus.messenger.api.v1.mappers
|
||||
|
||||
import kotlinx.datetime.Instant
|
||||
import ru.otus.messenger.api.v1.models.*
|
||||
import ru.otus.messenger.common.models.*
|
||||
import ru.otus.messenger.common.ChatContext
|
||||
import ru.otus.messenger.common.NONE
|
||||
import ru.otus.messenger.common.exceptions.UnknownChatCommand
|
||||
|
||||
|
||||
fun ChatContext.toTransportChat(): IResponse = when (val cmd = command) {
|
||||
ChatCommand.CREATE -> toTransportCreate()
|
||||
ChatCommand.READ -> toTransportRead()
|
||||
ChatCommand.DELETE -> toTransportDelete()
|
||||
ChatCommand.SEARCH -> toTransportSearch()
|
||||
ChatCommand.UPDATE -> toTransportUpdate()
|
||||
ChatCommand.NONE -> throw UnknownChatCommand(cmd)
|
||||
}
|
||||
|
||||
fun ChatContext.toTransportCreate() = ChatCreateResponse(
|
||||
result = state.toResult(),
|
||||
errors = errors.toTransportErrors(),
|
||||
chat = chatResponse.toTransportChat()
|
||||
)
|
||||
|
||||
fun ChatContext.toTransportRead() = ChatReadResponse(
|
||||
result = state.toResult(),
|
||||
errors = errors.toTransportErrors(),
|
||||
chat = chatResponse.toTransportChat()
|
||||
)
|
||||
|
||||
fun ChatContext.toTransportDelete() = ChatDeleteResponse(
|
||||
result = state.toResult(),
|
||||
errors = errors.toTransportErrors(),
|
||||
)
|
||||
|
||||
fun ChatContext.toTransportSearch() = ChatSearchResponse(
|
||||
result = state.toResult(),
|
||||
errors = errors.toTransportErrors(),
|
||||
chats = chatsResponse.toTransportChats()
|
||||
)
|
||||
|
||||
fun ChatContext.toTransportUpdate() = ChatUpdateResponse(
|
||||
result = state.toResult(),
|
||||
errors = errors.toTransportErrors(),
|
||||
chat = chatResponse.toTransportChat()
|
||||
)
|
||||
|
||||
private fun MessengerChat.toTransportChat(): Chat = Chat(
|
||||
id = id.takeIf { it != ChatId.NONE }?.asString(),
|
||||
title = title.takeIf { it.isNotBlank() },
|
||||
description = description.takeIf { it.isNotBlank() },
|
||||
ownerId = ownerId.takeIf { it != ChatOwnerId.NONE }?.asString(),
|
||||
type = type.toTransportChat(),
|
||||
mode = mode.toTransportChat(),
|
||||
participants = participants.toTransportChat(),
|
||||
createdAt = createdAt.takeIf { it != Instant.NONE }?.toString(),
|
||||
updatedAt = updatedAt.takeIf { it != Instant.NONE }?.toString(),
|
||||
isArchived = isArchived.toTransportChat(),
|
||||
metadata = metadata.toTransportChat(),
|
||||
)
|
||||
|
||||
private fun List<MessengerChat>.toTransportChats(): List<Chat>? = this
|
||||
.map { it.toTransportChat() }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
|
||||
private fun ChatType.toTransportChat() = when (this) {
|
||||
ChatType.PRIVATE -> Chat.Type.PRIVATE
|
||||
ChatType.GROUP -> Chat.Type.GROUP
|
||||
ChatType.CHANNEL -> Chat.Type.CHANNEL
|
||||
ChatType.NONE -> null
|
||||
}
|
||||
|
||||
private fun ChatMode.toTransportChat() = when (this) {
|
||||
ChatMode.PERSONAL -> Chat.Mode.PERSONAL
|
||||
ChatMode.WORK -> Chat.Mode.WORK
|
||||
ChatMode.NONE -> null
|
||||
}
|
||||
|
||||
private fun ChatArchiveFlag.toTransportChat() = this.asBoolean()
|
||||
|
||||
private fun ChatMetadata.toTransportChat() = this.asString()
|
||||
|
||||
private fun MutableSet<ChatUserId>.toTransportChat(): Set<String>? = this
|
||||
.map { it.asString() }
|
||||
.toSet()
|
||||
.takeIf { it.isNotEmpty() }
|
||||
|
||||
private fun List<ChatError>.toTransportErrors(): List<Error>? = this
|
||||
.map { it.toTransportChat() }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
|
||||
private fun ChatError.toTransportChat() = Error(
|
||||
code = code.takeIf { it.isNotBlank() },
|
||||
group = group.takeIf { it.isNotBlank() },
|
||||
fieldName = field.takeIf { it.isNotBlank() },
|
||||
message = message.takeIf { it.isNotBlank() },
|
||||
)
|
||||
|
||||
private fun ChatState.toResult(): ResponseResult? = when (this) {
|
||||
ChatState.RUNNING -> ResponseResult.SUCCESS
|
||||
ChatState.FAILING -> ResponseResult.ERROR
|
||||
ChatState.FINISHING -> ResponseResult.SUCCESS
|
||||
ChatState.NONE -> null
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
package ru.otus.messenger.api.v1.mappers.exceptions
|
||||
|
||||
class UnknownRequestClass(clazz: Class<*>) : RuntimeException("Class $clazz cannot be mapped")
|
||||
@ -0,0 +1,86 @@
|
||||
import java.util.UUID
|
||||
import kotlin.test.assertEquals
|
||||
import org.junit.Test
|
||||
import ru.otus.messenger.api.v1.mappers.fromTransport
|
||||
import ru.otus.messenger.api.v1.mappers.toTransportChat
|
||||
import ru.otus.messenger.api.v1.models.Chat
|
||||
import ru.otus.messenger.api.v1.models.ChatCreateRequest
|
||||
import ru.otus.messenger.api.v1.models.ChatCreateRequestAllOfChat
|
||||
import ru.otus.messenger.api.v1.models.ChatCreateResponse
|
||||
import ru.otus.messenger.api.v1.models.Debug
|
||||
import ru.otus.messenger.api.v1.models.DebugMode
|
||||
import ru.otus.messenger.api.v1.models.DebugStubs
|
||||
import ru.otus.messenger.common.ChatContext
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatError
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.models.RequestId
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.stubs.Stubs
|
||||
|
||||
class MapperCreateTest {
|
||||
@Test
|
||||
fun fromTransport() {
|
||||
val req = ChatCreateRequest(
|
||||
debug = Debug(
|
||||
mode = DebugMode.STUB,
|
||||
stub = DebugStubs.SUCCESS
|
||||
),
|
||||
chat = ChatCreateRequestAllOfChat(
|
||||
title = "title",
|
||||
description = "description",
|
||||
type = ChatCreateRequestAllOfChat.Type.PRIVATE,
|
||||
mode = ChatCreateRequestAllOfChat.Mode.PERSONAL,
|
||||
ownerId = UUID.randomUUID().toString(),
|
||||
)
|
||||
)
|
||||
|
||||
val context = ChatContext()
|
||||
context.fromTransport(req)
|
||||
|
||||
assertEquals(Stubs.SUCCESS, context.stubCase)
|
||||
assertEquals(WorkMode.STUB, context.workMode)
|
||||
assertEquals("title", context.chatRequest.title)
|
||||
assertEquals(ChatType.PRIVATE, context.chatRequest.type)
|
||||
assertEquals(ChatMode.PERSONAL, context.chatRequest.mode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toTransport() {
|
||||
val context = ChatContext(
|
||||
requestId = RequestId(UUID.randomUUID().toString()),
|
||||
command = ChatCommand.CREATE,
|
||||
state = ChatState.RUNNING,
|
||||
chatResponse = MessengerChat(
|
||||
title = "title",
|
||||
description = "description",
|
||||
type = ChatType.PRIVATE,
|
||||
mode = ChatMode.PERSONAL,
|
||||
ownerId = ChatOwnerId(UUID.randomUUID().toString()),
|
||||
),
|
||||
errors = mutableListOf(
|
||||
ChatError(
|
||||
code = "err",
|
||||
group = "request",
|
||||
field = "title",
|
||||
message = "wrong title",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val req = context.toTransportChat() as ChatCreateResponse
|
||||
|
||||
assertEquals("title", req.chat?.title)
|
||||
assertEquals(Chat.Type.PRIVATE, req.chat?.type)
|
||||
assertEquals(Chat.Mode.PERSONAL, req.chat?.mode)
|
||||
assertEquals(1, req.errors?.size)
|
||||
assertEquals("err", req.errors?.firstOrNull()?.code)
|
||||
assertEquals("request", req.errors?.firstOrNull()?.group)
|
||||
assertEquals("title", req.errors?.firstOrNull()?.fieldName)
|
||||
assertEquals("wrong title", req.errors?.firstOrNull()?.message)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
import java.util.UUID
|
||||
import kotlin.test.assertEquals
|
||||
import org.junit.Test
|
||||
import ru.otus.messenger.api.v1.mappers.fromTransport
|
||||
import ru.otus.messenger.api.v1.mappers.toTransportChat
|
||||
import ru.otus.messenger.api.v1.models.ChatDeleteRequest
|
||||
import ru.otus.messenger.api.v1.models.ChatDeleteResponse
|
||||
import ru.otus.messenger.api.v1.models.Debug
|
||||
import ru.otus.messenger.api.v1.models.DebugMode
|
||||
import ru.otus.messenger.api.v1.models.DebugStubs
|
||||
import ru.otus.messenger.api.v1.models.ResponseResult
|
||||
import ru.otus.messenger.common.ChatContext
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatError
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.RequestId
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.stubs.Stubs
|
||||
|
||||
class MapperDeleteTest {
|
||||
@Test
|
||||
fun fromTransport() {
|
||||
val req = ChatDeleteRequest(
|
||||
debug = Debug(
|
||||
mode = DebugMode.STUB,
|
||||
stub = DebugStubs.SUCCESS
|
||||
),
|
||||
chatId = "chat-id",
|
||||
)
|
||||
|
||||
val context = ChatContext()
|
||||
context.fromTransport(req)
|
||||
|
||||
assertEquals(Stubs.SUCCESS, context.stubCase)
|
||||
assertEquals(WorkMode.STUB, context.workMode)
|
||||
assertEquals("", context.chatRequest.title)
|
||||
assertEquals("chat-id", context.chatRequest.id.asString())
|
||||
assertEquals(ChatType.NONE, context.chatRequest.type)
|
||||
assertEquals(ChatMode.NONE, context.chatRequest.mode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toTransport() {
|
||||
val context = ChatContext(
|
||||
requestId = RequestId(UUID.randomUUID().toString()),
|
||||
command = ChatCommand.DELETE,
|
||||
state = ChatState.RUNNING,
|
||||
errors = mutableListOf(
|
||||
ChatError(
|
||||
code = "err",
|
||||
group = "request",
|
||||
field = "title",
|
||||
message = "wrong title",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val req = context.toTransportChat() as ChatDeleteResponse
|
||||
|
||||
assertEquals(ResponseResult.SUCCESS, req.result)
|
||||
assertEquals(1, req.errors?.size)
|
||||
assertEquals("err", req.errors?.firstOrNull()?.code)
|
||||
assertEquals("request", req.errors?.firstOrNull()?.group)
|
||||
assertEquals("title", req.errors?.firstOrNull()?.fieldName)
|
||||
assertEquals("wrong title", req.errors?.firstOrNull()?.message)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
import java.util.UUID
|
||||
import kotlin.test.assertEquals
|
||||
import org.junit.Test
|
||||
import ru.otus.messenger.api.v1.mappers.fromTransport
|
||||
import ru.otus.messenger.api.v1.mappers.toTransportChat
|
||||
import ru.otus.messenger.api.v1.models.Chat
|
||||
import ru.otus.messenger.api.v1.models.ChatReadRequest
|
||||
import ru.otus.messenger.api.v1.models.ChatReadResponse
|
||||
import ru.otus.messenger.api.v1.models.Debug
|
||||
import ru.otus.messenger.api.v1.models.DebugMode
|
||||
import ru.otus.messenger.api.v1.models.DebugStubs
|
||||
import ru.otus.messenger.common.ChatContext
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatError
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.models.RequestId
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.stubs.Stubs
|
||||
|
||||
class MapperReadTest {
|
||||
@Test
|
||||
fun fromTransport() {
|
||||
val req = ChatReadRequest(
|
||||
debug = Debug(
|
||||
mode = DebugMode.STUB,
|
||||
stub = DebugStubs.SUCCESS
|
||||
),
|
||||
chatId = "chat-id",
|
||||
)
|
||||
|
||||
val context = ChatContext()
|
||||
context.fromTransport(req)
|
||||
|
||||
assertEquals(Stubs.SUCCESS, context.stubCase)
|
||||
assertEquals(WorkMode.STUB, context.workMode)
|
||||
assertEquals("", context.chatRequest.title)
|
||||
assertEquals("chat-id", context.chatRequest.id.asString())
|
||||
assertEquals(ChatType.NONE, context.chatRequest.type)
|
||||
assertEquals(ChatMode.NONE, context.chatRequest.mode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toTransport() {
|
||||
val context = ChatContext(
|
||||
requestId = RequestId(UUID.randomUUID().toString()),
|
||||
command = ChatCommand.READ,
|
||||
state = ChatState.RUNNING,
|
||||
chatResponse = MessengerChat(
|
||||
title = "title",
|
||||
description = "description",
|
||||
type = ChatType.PRIVATE,
|
||||
mode = ChatMode.PERSONAL,
|
||||
ownerId = ChatOwnerId(UUID.randomUUID().toString()),
|
||||
),
|
||||
errors = mutableListOf(
|
||||
ChatError(
|
||||
code = "err",
|
||||
group = "request",
|
||||
field = "title",
|
||||
message = "wrong title",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val req = context.toTransportChat() as ChatReadResponse
|
||||
|
||||
assertEquals("title", req.chat?.title)
|
||||
assertEquals(Chat.Type.PRIVATE, req.chat?.type)
|
||||
assertEquals(Chat.Mode.PERSONAL, req.chat?.mode)
|
||||
assertEquals(1, req.errors?.size)
|
||||
assertEquals("err", req.errors?.firstOrNull()?.code)
|
||||
assertEquals("request", req.errors?.firstOrNull()?.group)
|
||||
assertEquals("title", req.errors?.firstOrNull()?.fieldName)
|
||||
assertEquals("wrong title", req.errors?.firstOrNull()?.message)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
import java.util.UUID
|
||||
import kotlin.test.assertEquals
|
||||
import org.junit.Test
|
||||
import ru.otus.messenger.api.v1.mappers.fromTransport
|
||||
import ru.otus.messenger.api.v1.mappers.toTransportChat
|
||||
import ru.otus.messenger.api.v1.models.Chat
|
||||
import ru.otus.messenger.api.v1.models.ChatSearchRequest
|
||||
import ru.otus.messenger.api.v1.models.ChatSearchRequestAllOfCriteria
|
||||
import ru.otus.messenger.api.v1.models.ChatSearchResponse
|
||||
import ru.otus.messenger.api.v1.models.Debug
|
||||
import ru.otus.messenger.api.v1.models.DebugMode
|
||||
import ru.otus.messenger.api.v1.models.DebugStubs
|
||||
import ru.otus.messenger.common.ChatContext
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatError
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.models.RequestId
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.stubs.Stubs
|
||||
|
||||
class MapperSearchTest {
|
||||
@Test
|
||||
fun fromTransport() {
|
||||
val req = ChatSearchRequest(
|
||||
debug = Debug(
|
||||
mode = DebugMode.STUB,
|
||||
stub = DebugStubs.SUCCESS
|
||||
),
|
||||
criteria = ChatSearchRequestAllOfCriteria(
|
||||
title = "title",
|
||||
type = ChatSearchRequestAllOfCriteria.Type.PRIVATE,
|
||||
mode = ChatSearchRequestAllOfCriteria.Mode.PERSONAL,
|
||||
participant = UUID.randomUUID().toString(),
|
||||
)
|
||||
)
|
||||
|
||||
val context = ChatContext()
|
||||
context.fromTransport(req)
|
||||
|
||||
assertEquals(Stubs.SUCCESS, context.stubCase)
|
||||
assertEquals(WorkMode.STUB, context.workMode)
|
||||
assertEquals("title", context.chatRequest.title)
|
||||
assertEquals(ChatType.PRIVATE, context.chatRequest.type)
|
||||
assertEquals(ChatMode.PERSONAL, context.chatRequest.mode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toTransport() {
|
||||
val context = ChatContext(
|
||||
requestId = RequestId(UUID.randomUUID().toString()),
|
||||
command = ChatCommand.SEARCH,
|
||||
state = ChatState.RUNNING,
|
||||
chatsResponse = mutableListOf(
|
||||
MessengerChat(
|
||||
title = "title",
|
||||
description = "description",
|
||||
type = ChatType.PRIVATE,
|
||||
mode = ChatMode.PERSONAL,
|
||||
ownerId = ChatOwnerId(UUID.randomUUID().toString()),
|
||||
),
|
||||
MessengerChat(
|
||||
title = "title-1",
|
||||
description = "description",
|
||||
type = ChatType.GROUP,
|
||||
mode = ChatMode.WORK,
|
||||
ownerId = ChatOwnerId(UUID.randomUUID().toString()),
|
||||
)
|
||||
),
|
||||
errors = mutableListOf(
|
||||
ChatError(
|
||||
code = "err",
|
||||
group = "request",
|
||||
field = "title",
|
||||
message = "wrong title",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val req = context.toTransportChat() as ChatSearchResponse
|
||||
|
||||
assertEquals("title", req.chats?.get(0)?.title)
|
||||
assertEquals("title-1", req.chats?.get(1)?.title)
|
||||
assertEquals(Chat.Type.PRIVATE, req.chats?.get(0)?.type)
|
||||
assertEquals(Chat.Type.GROUP, req.chats?.get(1)?.type)
|
||||
assertEquals(Chat.Mode.PERSONAL, req.chats?.get(0)?.mode)
|
||||
assertEquals(Chat.Mode.WORK, req.chats?.get(1)?.mode)
|
||||
assertEquals(1, req.errors?.size)
|
||||
assertEquals("err", req.errors?.firstOrNull()?.code)
|
||||
assertEquals("request", req.errors?.firstOrNull()?.group)
|
||||
assertEquals("title", req.errors?.firstOrNull()?.fieldName)
|
||||
assertEquals("wrong title", req.errors?.firstOrNull()?.message)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
import java.util.UUID
|
||||
import kotlin.test.assertEquals
|
||||
import org.junit.Test
|
||||
import ru.otus.messenger.api.v1.mappers.fromTransport
|
||||
import ru.otus.messenger.api.v1.mappers.toTransportChat
|
||||
import ru.otus.messenger.api.v1.models.Chat
|
||||
import ru.otus.messenger.api.v1.models.ChatUpdateRequest
|
||||
import ru.otus.messenger.api.v1.models.ChatUpdateRequestAllOfChat
|
||||
import ru.otus.messenger.api.v1.models.ChatUpdateResponse
|
||||
import ru.otus.messenger.api.v1.models.Debug
|
||||
import ru.otus.messenger.api.v1.models.DebugMode
|
||||
import ru.otus.messenger.api.v1.models.DebugStubs
|
||||
import ru.otus.messenger.common.ChatContext
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
import ru.otus.messenger.common.models.ChatError
|
||||
import ru.otus.messenger.common.models.ChatMode
|
||||
import ru.otus.messenger.common.models.ChatOwnerId
|
||||
import ru.otus.messenger.common.models.ChatState
|
||||
import ru.otus.messenger.common.models.ChatType
|
||||
import ru.otus.messenger.common.models.MessengerChat
|
||||
import ru.otus.messenger.common.models.RequestId
|
||||
import ru.otus.messenger.common.models.WorkMode
|
||||
import ru.otus.messenger.common.stubs.Stubs
|
||||
|
||||
class MapperUpdateTest {
|
||||
@Test
|
||||
fun fromTransport() {
|
||||
val req = ChatUpdateRequest(
|
||||
debug = Debug(
|
||||
mode = DebugMode.STUB,
|
||||
stub = DebugStubs.SUCCESS
|
||||
),
|
||||
chat = ChatUpdateRequestAllOfChat(
|
||||
chatId = "chat-id",
|
||||
title = "title",
|
||||
description = "description",
|
||||
mode = ChatUpdateRequestAllOfChat.Mode.PERSONAL,
|
||||
isArchived = false,
|
||||
metadata = """
|
||||
{
|
||||
"testParam": "test"
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
|
||||
val context = ChatContext()
|
||||
context.fromTransport(req)
|
||||
|
||||
assertEquals(Stubs.SUCCESS, context.stubCase)
|
||||
assertEquals(WorkMode.STUB, context.workMode)
|
||||
assertEquals("chat-id", context.chatRequest.id.asString())
|
||||
assertEquals("title", context.chatRequest.title)
|
||||
assertEquals(ChatType.NONE, context.chatRequest.type)
|
||||
assertEquals(ChatMode.PERSONAL, context.chatRequest.mode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toTransport() {
|
||||
val context = ChatContext(
|
||||
requestId = RequestId(UUID.randomUUID().toString()),
|
||||
command = ChatCommand.UPDATE,
|
||||
state = ChatState.RUNNING,
|
||||
chatResponse = MessengerChat(
|
||||
title = "title",
|
||||
description = "description",
|
||||
type = ChatType.PRIVATE,
|
||||
mode = ChatMode.PERSONAL,
|
||||
ownerId = ChatOwnerId(UUID.randomUUID().toString()),
|
||||
),
|
||||
errors = mutableListOf(
|
||||
ChatError(
|
||||
code = "err",
|
||||
group = "request",
|
||||
field = "title",
|
||||
message = "wrong title",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val req = context.toTransportChat() as ChatUpdateResponse
|
||||
|
||||
assertEquals("title", req.chat?.title)
|
||||
assertEquals(Chat.Type.PRIVATE, req.chat?.type)
|
||||
assertEquals(Chat.Mode.PERSONAL, req.chat?.mode)
|
||||
assertEquals(1, req.errors?.size)
|
||||
assertEquals("err", req.errors?.firstOrNull()?.code)
|
||||
assertEquals("request", req.errors?.firstOrNull()?.group)
|
||||
assertEquals("title", req.errors?.firstOrNull()?.fieldName)
|
||||
assertEquals("wrong title", req.errors?.firstOrNull()?.message)
|
||||
}
|
||||
}
|
||||
56
ok-messenger-be/ok-messenger-api-v1/build.gradle.kts
Normal file
56
ok-messenger-be/ok-messenger-api-v1/build.gradle.kts
Normal file
@ -0,0 +1,56 @@
|
||||
import kotlin.apply
|
||||
|
||||
plugins {
|
||||
id("build-jvm")
|
||||
alias(libs.plugins.openapi.generator)
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDir(layout.buildDirectory.dir("generate-resources/main/src/main/kotlin"))
|
||||
}
|
||||
}
|
||||
|
||||
openApiGenerate {
|
||||
val openapiGroup = "${rootProject.group}.api.v1"
|
||||
generatorName.set("kotlin")
|
||||
packageName.set(openapiGroup)
|
||||
apiPackage.set("$openapiGroup.api")
|
||||
modelPackage.set("$openapiGroup.models")
|
||||
inputSpec.set(rootProject.ext["spec-v1"] as String)
|
||||
|
||||
/**
|
||||
* Use only models
|
||||
* Doc: https://openapi-generator.tech/docs/globals
|
||||
*/
|
||||
globalProperties.apply {
|
||||
put("models", "")
|
||||
put("modelDocs", "false")
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional parameters from
|
||||
* https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/kotlin.md
|
||||
*/
|
||||
configOptions.set(
|
||||
mapOf(
|
||||
"dateLibrary" to "string",
|
||||
"enumPropertyNaming" to "UPPERCASE",
|
||||
"serializationLibrary" to "jackson",
|
||||
"collectionType" to "list"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(libs.kotlin.jackson.module)
|
||||
implementation(libs.kotlin.jackson.datatype)
|
||||
testImplementation(kotlin("test-junit"))
|
||||
}
|
||||
|
||||
tasks {
|
||||
compileKotlin {
|
||||
dependsOn(openApiGenerate)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package ru.otus.messenger.api.v1
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.MapperFeature
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||
import ru.otus.messenger.api.v1.models.IRequest
|
||||
import ru.otus.messenger.api.v1.models.IResponse
|
||||
|
||||
val apiV1Mapper: JsonMapper = JsonMapper.builder().run {
|
||||
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
enable(MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL)
|
||||
build()
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun apiV1RequestSerialize(request: IRequest): String = apiV1Mapper.writeValueAsString(request)
|
||||
|
||||
@Suppress("UNCHECKED_CAST", "unused")
|
||||
fun <T : IRequest> apiV1RequestDeserialize(json: String): T =
|
||||
apiV1Mapper.readValue(json, IRequest::class.java) as T
|
||||
|
||||
@Suppress("unused")
|
||||
fun apiV1ResponseSerialize(response: IResponse): String = apiV1Mapper.writeValueAsString(response)
|
||||
|
||||
@Suppress("UNCHECKED_CAST", "unused")
|
||||
fun <T : IResponse> apiV1ResponseDeserialize(json: String): T =
|
||||
apiV1Mapper.readValue(json, IResponse::class.java) as T
|
||||
@ -0,0 +1,56 @@
|
||||
package ru.otus.messenger.api.v1
|
||||
|
||||
import ru.otus.messenger.api.v1.models.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertContains
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class RequestV1SerializationTest {
|
||||
private val request = ChatCreateRequest(
|
||||
debug = Debug(
|
||||
mode = DebugMode.STUB,
|
||||
stub = DebugStubs.VALUE_ERROR
|
||||
),
|
||||
requestType = "create",
|
||||
chat = ChatCreateRequestAllOfChat(
|
||||
title = "New chat",
|
||||
type = ChatCreateRequestAllOfChat.Type.GROUP,
|
||||
mode = ChatCreateRequestAllOfChat.Mode.PERSONAL,
|
||||
participants = emptyList(),
|
||||
metadata = """
|
||||
{
|
||||
"isOwner": true,
|
||||
"testMeta": "qwerty",
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun serialize() {
|
||||
val json = apiV1RequestSerialize(request)
|
||||
|
||||
assertContains(json, Regex("\"title\":\\s*\"${request.chat?.title}\""))
|
||||
assertContains(json, Regex("\"mode\":\\s*\"${request.debug?.mode}\""))
|
||||
assertContains(json, Regex("\"stub\":\\s*\"${request.debug?.stub}\""))
|
||||
assertContains(json, Regex("\"requestType\":\\s*\"${request.requestType}\""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deserialize() {
|
||||
val json = apiV1RequestSerialize(request)
|
||||
val obj = apiV1RequestDeserialize<ChatCreateRequest>(json)
|
||||
|
||||
assertEquals(request, obj)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deserializeNaked() {
|
||||
val jsonString = """
|
||||
{ "requestType": "create" }
|
||||
""".trimIndent()
|
||||
val obj = apiV1RequestDeserialize<ChatCreateRequest>(jsonString)
|
||||
|
||||
assertEquals(null, obj.chat)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package ru.otus.messenger.api.v1
|
||||
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertContains
|
||||
import kotlin.test.assertEquals
|
||||
import ru.otus.messenger.api.v1.models.*
|
||||
|
||||
class ResponseV1SerializationTest {
|
||||
private val response = ChatCreateResponse(
|
||||
responseType = "create",
|
||||
result = ResponseResult.SUCCESS,
|
||||
chat = Chat(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Test chat title",
|
||||
type = Chat.Type.CHANNEL,
|
||||
mode = Chat.Mode.WORK,
|
||||
participants = emptyList(),
|
||||
createdAt = Instant.now().toString(),
|
||||
isArchived = false,
|
||||
metadata = null
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun serialize() {
|
||||
val json = apiV1ResponseSerialize(response)
|
||||
|
||||
assertContains(json, Regex("\"title\":\\s*\"${response.chat?.title}\""))
|
||||
assertContains(json, Regex("\"id\":\\s*\"${response.chat?.id}\""))
|
||||
assertContains(json, Regex("\"responseType\":\\s*\"${response.responseType}\""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deserialize() {
|
||||
val json = apiV1ResponseSerialize(response)
|
||||
val obj = apiV1ResponseDeserialize<ChatCreateResponse>(json)
|
||||
|
||||
assertEquals(response, obj)
|
||||
}
|
||||
}
|
||||
3
ok-messenger-be/ok-messenger-common/README.md
Normal file
3
ok-messenger-be/ok-messenger-common/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Модуль `ok-messenger-common`
|
||||
|
||||
Содержит объекты, которые используются всеми остальными модулями проекта.
|
||||
20
ok-messenger-be/ok-messenger-common/build.gradle.kts
Normal file
20
ok-messenger-be/ok-messenger-common/build.gradle.kts
Normal file
@ -0,0 +1,20 @@
|
||||
plugins {
|
||||
id("build-jvm")
|
||||
}
|
||||
|
||||
group = rootProject.group
|
||||
version = rootProject.version
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDir("src/commonMain/kotlin")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(libs.kotlin.datetime)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
testImplementation(kotlin("test-junit"))
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package ru.otus.messenger.common
|
||||
|
||||
import kotlinx.datetime.Instant
|
||||
import ru.otus.messenger.common.models.*
|
||||
import ru.otus.messenger.common.stubs.Stubs
|
||||
|
||||
data class ChatContext(
|
||||
var command: ChatCommand = ChatCommand.NONE,
|
||||
var state: ChatState = ChatState.NONE,
|
||||
val errors: MutableList<ChatError> = mutableListOf(),
|
||||
|
||||
var workMode: WorkMode = WorkMode.PROD,
|
||||
var stubCase: Stubs = Stubs.NONE,
|
||||
|
||||
var requestId: RequestId = RequestId.NONE,
|
||||
var timeStart: Instant = Instant.NONE,
|
||||
var chatRequest: MessengerChat = MessengerChat(),
|
||||
var chatFilterRequest: ChatSearchFilter = ChatSearchFilter(),
|
||||
|
||||
var chatResponse: MessengerChat = MessengerChat(),
|
||||
var chatsResponse: MutableList<MessengerChat> = mutableListOf(),
|
||||
)
|
||||
@ -0,0 +1,7 @@
|
||||
package ru.otus.messenger.common
|
||||
|
||||
import kotlinx.datetime.Instant
|
||||
|
||||
private val INSTANT_NONE = Instant.fromEpochMilliseconds(Long.MIN_VALUE)
|
||||
val Instant.Companion.NONE
|
||||
get() = INSTANT_NONE
|
||||
@ -0,0 +1,5 @@
|
||||
package ru.otus.messenger.common.exceptions
|
||||
|
||||
import ru.otus.messenger.common.models.ChatCommand
|
||||
|
||||
class UnknownChatCommand(command: ChatCommand) : Throwable("Wrong command $command at mapping toTransport stage")
|
||||
@ -0,0 +1,14 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
import kotlin.jvm.JvmInline
|
||||
|
||||
@JvmInline
|
||||
value class ChatArchiveFlag(private val flag: Boolean) {
|
||||
fun asString() = flag.toString()
|
||||
|
||||
fun asBoolean() = flag
|
||||
|
||||
companion object {
|
||||
val NONE = ChatArchiveFlag(false)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
enum class ChatCommand {
|
||||
NONE,
|
||||
CREATE,
|
||||
READ,
|
||||
DELETE,
|
||||
SEARCH,
|
||||
UPDATE,
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
data class ChatError(
|
||||
val code: String = "",
|
||||
val group: String = "",
|
||||
val field: String = "",
|
||||
val message: String = "",
|
||||
val exception: Throwable? = null,
|
||||
)
|
||||
@ -0,0 +1,12 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
import kotlin.jvm.JvmInline
|
||||
|
||||
@JvmInline
|
||||
value class ChatId(private val id: String) {
|
||||
fun asString() = id
|
||||
|
||||
companion object {
|
||||
val NONE = ChatId("")
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
import kotlin.jvm.JvmInline
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
|
||||
@JvmInline
|
||||
value class ChatMetadata(private val metadata: JsonObject) {
|
||||
fun asString() = metadata.toString()
|
||||
|
||||
companion object {
|
||||
val NONE = ChatMetadata(buildJsonObject {})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
enum class ChatMode {
|
||||
NONE,
|
||||
PERSONAL,
|
||||
WORK,
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
import kotlin.jvm.JvmInline
|
||||
|
||||
@JvmInline
|
||||
value class ChatOwnerId(private val id: String) {
|
||||
fun asString() = id
|
||||
|
||||
companion object {
|
||||
val NONE = ChatOwnerId("")
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
data class ChatSearchFilter(
|
||||
var searchString: String = "",
|
||||
var ownerId: ChatOwnerId = ChatOwnerId.NONE,
|
||||
var type: ChatType = ChatType.NONE,
|
||||
)
|
||||
@ -0,0 +1,8 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
enum class ChatState {
|
||||
NONE,
|
||||
RUNNING,
|
||||
FAILING,
|
||||
FINISHING,
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
enum class ChatType {
|
||||
NONE,
|
||||
PRIVATE,
|
||||
CHANNEL,
|
||||
GROUP,
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
import kotlin.jvm.JvmInline
|
||||
|
||||
@JvmInline
|
||||
value class ChatUserId(private val id: String) {
|
||||
fun asString() = id
|
||||
|
||||
companion object {
|
||||
val NONE = ChatUserId("")
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
import kotlinx.datetime.Instant
|
||||
import ru.otus.messenger.common.NONE
|
||||
|
||||
data class MessengerChat(
|
||||
var id: ChatId = ChatId.NONE,
|
||||
var title: String = "",
|
||||
var description: String = "",
|
||||
var type: ChatType = ChatType.NONE,
|
||||
var mode: ChatMode = ChatMode.NONE,
|
||||
var ownerId: ChatOwnerId = ChatOwnerId.NONE,
|
||||
val participants: MutableSet<ChatUserId> = mutableSetOf(),
|
||||
var createdAt: Instant = Instant.NONE,
|
||||
var updatedAt: Instant = Instant.NONE,
|
||||
var isArchived: ChatArchiveFlag = ChatArchiveFlag.NONE,
|
||||
var metadata: ChatMetadata = ChatMetadata.NONE,
|
||||
) {
|
||||
fun isEmpty() = this == NONE
|
||||
|
||||
companion object {
|
||||
private val NONE = MessengerChat()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
import kotlin.jvm.JvmInline
|
||||
|
||||
@JvmInline
|
||||
value class RequestId(private val id: String) {
|
||||
fun asString() = id
|
||||
|
||||
companion object {
|
||||
val NONE = RequestId("")
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package ru.otus.messenger.common.models
|
||||
|
||||
enum class WorkMode {
|
||||
PROD,
|
||||
TEST,
|
||||
STUB,
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package ru.otus.messenger.common.stubs
|
||||
|
||||
enum class Stubs {
|
||||
NONE,
|
||||
SUCCESS,
|
||||
NOT_FOUND,
|
||||
CANNOT_DELETE,
|
||||
MISSING_DATA,
|
||||
VALUE_ERROR,
|
||||
DB_ERROR,
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
plugins {
|
||||
id("build-jvm")
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
fun main() {
|
||||
print("Hello world")
|
||||
}
|
||||
@ -23,4 +23,6 @@ rootProject.name = "ok-messenger-be"
|
||||
|
||||
//enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||
|
||||
include(":ok-messenger-tmp")
|
||||
include(":ok-messenger-api-v1")
|
||||
include(":ok-messenger-api-v1-mappers")
|
||||
include(":ok-messenger-common")
|
||||
|
||||
403
specs/specs-v1.yaml
Normal file
403
specs/specs-v1.yaml
Normal file
@ -0,0 +1,403 @@
|
||||
openapi: 3.0.4
|
||||
info:
|
||||
title: "Messenger ${VERSION_APP}"
|
||||
version: 1.0.0
|
||||
description: Программное обеспечение для обмена информацией между пользователями.
|
||||
servers:
|
||||
- url: http://localhost:8080/api/v1
|
||||
paths:
|
||||
/chat/create:
|
||||
post:
|
||||
tags:
|
||||
- Chat
|
||||
summary: Создать новый чат
|
||||
operationId: createChat
|
||||
requestBody:
|
||||
description: Запрос на создание чата
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatCreateRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Чат успешно создан
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatCreateResponse'
|
||||
/chat/read:
|
||||
post:
|
||||
tags:
|
||||
- Chat
|
||||
summary: Получить чат по ID
|
||||
operationId: readChat
|
||||
requestBody:
|
||||
description: Запрос на получение чата
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatReadRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Информация о чате
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatReadResponse'
|
||||
/chat/search:
|
||||
post:
|
||||
tags:
|
||||
- Chat
|
||||
summary: Поиск чатов
|
||||
operationId: searchChats
|
||||
requestBody:
|
||||
description: Запрос на поиск чатов по заданным критериям
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatSearchRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Список найденных чатов
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatSearchResponse'
|
||||
/chat/update:
|
||||
post:
|
||||
tags:
|
||||
- Chat
|
||||
summary: Обновить чат
|
||||
operationId: updateChat
|
||||
requestBody:
|
||||
description: Запрос на обновление информации о чате
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatUpdateRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Чат успешно обновлён
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatUpdateResponse'
|
||||
/chat/delete:
|
||||
post:
|
||||
tags:
|
||||
- Chat
|
||||
summary: Архивировать (удалить) чат
|
||||
operationId: deleteChat
|
||||
requestBody:
|
||||
description: Запрос на архивирование чата
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatDeleteRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Чат успешно архивирован
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChatDeleteResponse'
|
||||
components:
|
||||
schemas:
|
||||
Error:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
group:
|
||||
type: string
|
||||
fieldName:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
|
||||
ResponseResult:
|
||||
type: string
|
||||
enum:
|
||||
- success
|
||||
- error
|
||||
|
||||
# Интерфейс реквеста
|
||||
IRequest:
|
||||
type: object
|
||||
description: Базовый интерфейс для всех запросов
|
||||
properties:
|
||||
requestType:
|
||||
type: string
|
||||
description: Поле-дескриминатор для вычисления типа запроса
|
||||
example: create
|
||||
discriminator:
|
||||
propertyName: requestType
|
||||
mapping:
|
||||
create: '#/components/schemas/ChatCreateRequest'
|
||||
read: '#/components/schemas/ChatReadRequest'
|
||||
search: '#/components/schemas/ChatSearchRequest'
|
||||
update: '#/components/schemas/ChatUpdateRequest'
|
||||
delete: '#/components/schemas/ChatDeleteRequest'
|
||||
|
||||
# Интерфейс респонса
|
||||
IResponse:
|
||||
type: object
|
||||
description: Базовый интерфейс для всех ответов
|
||||
properties:
|
||||
responseType:
|
||||
type: string
|
||||
description: Поле-дескриминатор для вычисления типа запроса
|
||||
example: create
|
||||
result:
|
||||
$ref: '#/components/schemas/ResponseResult'
|
||||
errors:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Error'
|
||||
discriminator:
|
||||
propertyName: responseType
|
||||
mapping:
|
||||
create: '#/components/schemas/ChatCreateResponse'
|
||||
read: '#/components/schemas/ChatReadResponse'
|
||||
search: '#/components/schemas/ChatSearchResponse'
|
||||
update: '#/components/schemas/ChatUpdateResponse'
|
||||
delete: '#/components/schemas/ChatDeleteResponse'
|
||||
|
||||
# Основная схема объекта Chat
|
||||
Chat:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: Уникальный идентификатор чата (UUID)
|
||||
title:
|
||||
type: string
|
||||
description: Название чата
|
||||
description:
|
||||
type: string
|
||||
description: Описание чата
|
||||
type:
|
||||
type: string
|
||||
enum: [private, group, channel]
|
||||
description: Тип чата
|
||||
mode:
|
||||
type: string
|
||||
enum: [work, personal]
|
||||
description: Режим чата
|
||||
owner_id:
|
||||
type: string
|
||||
description: Уникальный идентификатор владельца (UUID)
|
||||
participants:
|
||||
type: array
|
||||
uniqueItems: true
|
||||
items:
|
||||
type: string
|
||||
description: Список участников (user IDs)
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Дата создания чата
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Дата обновления чата
|
||||
is_archived:
|
||||
type: boolean
|
||||
description: Флаг архивации чата
|
||||
metadata:
|
||||
type: string
|
||||
format: json
|
||||
description: Дополнительные данные (например, project_id, jira_link и пр.)
|
||||
|
||||
# --- Создание чата ---
|
||||
ChatCreateRequest:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/IRequest'
|
||||
- $ref: '#/components/schemas/RequestDebug'
|
||||
- type: object
|
||||
properties:
|
||||
chat:
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
description: Название чата
|
||||
description:
|
||||
type: string
|
||||
description: Описание чата
|
||||
type:
|
||||
type: string
|
||||
enum: [private, group, channel]
|
||||
description: Тип чата
|
||||
mode:
|
||||
type: string
|
||||
enum: [work, personal]
|
||||
description: Режим чата
|
||||
owner_id:
|
||||
type: string
|
||||
description: Уникальный идентификатор владельца (UUID)
|
||||
participants:
|
||||
type: array
|
||||
uniqueItems: true
|
||||
items:
|
||||
type: string
|
||||
description: Список идентификаторов участников
|
||||
metadata:
|
||||
type: string
|
||||
format: json
|
||||
description: Дополнительные данные (необязательно)
|
||||
|
||||
ChatCreateResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/IResponse'
|
||||
- type: object
|
||||
properties:
|
||||
chat:
|
||||
$ref: '#/components/schemas/Chat'
|
||||
|
||||
# --- Чтение чата ---
|
||||
ChatReadRequest:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/IRequest'
|
||||
- $ref: '#/components/schemas/RequestDebug'
|
||||
- type: object
|
||||
properties:
|
||||
chatId:
|
||||
type: string
|
||||
description: Идентификатор чата для чтения
|
||||
example: string
|
||||
|
||||
ChatReadResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/IResponse'
|
||||
- type: object
|
||||
properties:
|
||||
chat:
|
||||
$ref: '#/components/schemas/Chat'
|
||||
|
||||
# --- Поиск чатов ---
|
||||
ChatSearchRequest:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/IRequest'
|
||||
- $ref: '#/components/schemas/RequestDebug'
|
||||
- type: object
|
||||
properties:
|
||||
criteria:
|
||||
type: object
|
||||
description: Критерии поиска чатов
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
description: Поиск по названию
|
||||
type:
|
||||
type: string
|
||||
enum: [private, group, channel]
|
||||
description: Поиск по типу чата
|
||||
mode:
|
||||
type: string
|
||||
enum: [work, personal]
|
||||
description: Поиск по режиму чата
|
||||
participant:
|
||||
type: string
|
||||
description: Поиск по участнику (ID пользователя)
|
||||
|
||||
ChatSearchResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/IResponse'
|
||||
- type: object
|
||||
properties:
|
||||
chats:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Chat'
|
||||
|
||||
# --- Обновление чата ---
|
||||
ChatUpdateRequest:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/IRequest'
|
||||
- $ref: '#/components/schemas/RequestDebug'
|
||||
- type: object
|
||||
properties:
|
||||
chat:
|
||||
type: object
|
||||
properties:
|
||||
chatId:
|
||||
type: string
|
||||
description: Идентификатор чата для обновления
|
||||
title:
|
||||
type: string
|
||||
description: Новое название чата
|
||||
description:
|
||||
type: string
|
||||
description: Новое описание чата
|
||||
mode:
|
||||
type: string
|
||||
enum: [work, personal]
|
||||
description: Новый режим чата
|
||||
is_archived:
|
||||
type: boolean
|
||||
description: Флаг архивации (если требуется)
|
||||
metadata:
|
||||
type: string
|
||||
format: json
|
||||
description: Дополнительные данные
|
||||
|
||||
ChatUpdateResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/IResponse'
|
||||
- type: object
|
||||
properties:
|
||||
chat:
|
||||
$ref: '#/components/schemas/Chat'
|
||||
|
||||
# --- Архивирование (удаление) чата ---
|
||||
ChatDeleteRequest:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/IRequest'
|
||||
- $ref: '#/components/schemas/RequestDebug'
|
||||
- type: object
|
||||
properties:
|
||||
chatId:
|
||||
type: string
|
||||
description: Идентификатор чата для архивирования
|
||||
|
||||
ChatDeleteResponse:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/IResponse'
|
||||
|
||||
# STUBS ======================
|
||||
DebugMode:
|
||||
type: string
|
||||
enum:
|
||||
- prod
|
||||
- test
|
||||
- stub
|
||||
|
||||
RequestDebug:
|
||||
type: object
|
||||
properties:
|
||||
debug:
|
||||
$ref: '#/components/schemas/Debug'
|
||||
|
||||
Debug:
|
||||
type: object
|
||||
properties:
|
||||
mode:
|
||||
$ref: '#/components/schemas/DebugMode'
|
||||
stub:
|
||||
$ref: '#/components/schemas/DebugStubs'
|
||||
|
||||
DebugStubs:
|
||||
type: string
|
||||
description: Перечисления всех стабов
|
||||
enum:
|
||||
- success
|
||||
- notFound
|
||||
- cannotDelete
|
||||
- valueError
|
||||
- missingData
|
||||
Reference in New Issue
Block a user