Skip to content

Commit

Permalink
add storage and update context
Browse files Browse the repository at this point in the history
  • Loading branch information
Данила Беляков authored and Данила Беляков committed Nov 14, 2024
1 parent 2e2cb34 commit d86f387
Show file tree
Hide file tree
Showing 30 changed files with 667 additions and 204 deletions.
3 changes: 3 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions alice-ktx/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ dependencies {
implementation("ch.qos.logback:logback-classic:1.5.6")

implementation("io.ktor:ktor-serialization-kotlinx-json-jvm")

testImplementation("junit:junit:4.13.2")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0")
}

kotlin {
Expand Down
15 changes: 8 additions & 7 deletions alice-ktx/src/main/kotlin/com/github/alice/ktx/Dispatch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,28 @@ fun Skill.Builder.dispatch(body: Dispatcher.() -> Unit) {
* @param fsmStrategy Стратегия конечного автомата состояний (FSM), используемая для управления состояниями.
*/
class Dispatcher internal constructor(
val fsmStrategy: FSMStrategy,
internal val fsmStrategy: FSMStrategy,
val dialogApi: DialogApi? = null,
internal val fsmContext: (message: MessageRequest) -> FSMContext
internal val fsmContext: (message: MessageRequest) -> FSMContext,
internal val enableApiStorage: Boolean = false
) {
internal val commandHandlers = linkedSetOf<Handler>()
internal val networkErrorHandlers = linkedSetOf<NetworkErrorHandler>()
internal val middlewares = linkedMapOf<MiddlewareType, MutableList<Middleware>>()
internal val middlewares = mutableMapOf<MiddlewareType, LinkedHashSet<Middleware>>()

init {
MiddlewareType.entries.forEach { middlewares[it] = mutableListOf() }
MiddlewareType.entries.forEach { middlewares[it] = linkedSetOf() }
}

internal fun addHandler(handler: Handler) {
fun addHandler(handler: Handler) {
commandHandlers.add(handler)
}

internal fun addMiddleware(middleware: Middleware, type: MiddlewareType) {
fun addMiddleware(middleware: Middleware, type: MiddlewareType) {
middlewares[type]?.add(middleware)
}

internal fun addNetworkErrorHandler(handler: NetworkErrorHandler) {
fun addNetworkErrorHandler(handler: NetworkErrorHandler) {
networkErrorHandlers.add(handler)
}
}
23 changes: 13 additions & 10 deletions alice-ktx/src/main/kotlin/com/github/alice/ktx/Skill.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import com.github.alice.ktx.models.response.MessageResponse
import com.github.alice.ktx.server.WebServer
import com.github.alice.ktx.server.WebServerResponseListener
import com.github.alice.ktx.state.FSMContext
import com.github.alice.ktx.state.impl.KotlinxSerializationFSMContext
import com.github.alice.ktx.state.impl.BaseFSMContext
import com.github.alice.ktx.storage.apiStorage.EnableApiStorage
import com.github.alice.ktx.storage.impl.memoryStorage
import kotlinx.serialization.json.Json

/**
Expand Down Expand Up @@ -43,22 +45,23 @@ class Skill internal constructor(
var fsmStrategy: FSMStrategy = FSMStrategy.USER
internal var dispatcherConfiguration: Dispatcher.() -> Unit = { }

var storage = memoryStorage { }

var fsmContext: (message: MessageRequest) -> FSMContext = { message ->
KotlinxSerializationFSMContext(message, fsmStrategy, json)
BaseFSMContext(storage, id, fsmStrategy, message)
}

internal fun build(body: Builder.() -> Unit): Skill {
body()

val dispatcher = Dispatcher(
fsmStrategy = fsmStrategy,
dialogApi = dialogApi,
fsmContext = fsmContext
).apply(dispatcherConfiguration)

return Skill(
webServer = webServer,
dispatcher = dispatcher
dispatcher = Dispatcher(
fsmStrategy = fsmStrategy,
dialogApi = dialogApi,
fsmContext = fsmContext,
enableApiStorage = storage.javaClass.isAnnotationPresent(EnableApiStorage::class.java)
).apply(dispatcherConfiguration)
)
}
}
Expand All @@ -80,7 +83,7 @@ class Skill internal constructor(
override suspend fun messageHandle(model: MessageRequest): MessageResponse? {
runMiddlewares(model, MiddlewareType.OUTER)?.let { return it }
dispatcher.commandHandlers.forEach { handler ->
if(handler.event(model)) {
if (handler.event(model)) {
runMiddlewares(model, MiddlewareType.INNER)?.let { return it }
return handler.handle(model)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fun Dispatcher.message(
val messageHandler = MessageHandler(
eventBlock = { message ->
val eventMessage = EventMessage(
context = request(message).state,
context = request(message).context,
message = message
)
event(eventMessage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fun Dispatcher.buttonPressed(
event(
EventButtonPressed(
payload = message.request.payload!!,
context = request(message).state
context = request(message).context
)
)
},
Expand Down
15 changes: 12 additions & 3 deletions alice-ktx/src/main/kotlin/com/github/alice/ktx/models/Request.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,24 @@ package com.github.alice.ktx.models
import com.github.alice.ktx.Dispatcher
import com.github.alice.ktx.models.request.MessageRequest
import com.github.alice.ktx.state.FSMContext
import com.github.alice.ktx.state.MutableFSMContext
import com.github.alice.ktx.storage.apiStorage.ApiStorageDetails

internal suspend fun Dispatcher.request(message: MessageRequest): Request {
val context = fsmContext(message)
context.init()

internal fun Dispatcher.request(message: MessageRequest): Request {
return Request(
message = message,
state = fsmContext(message)
context = context,
fsmStrategy = fsmStrategy,
enableApiStorage = enableApiStorage
)
}

data class Request(
val message: MessageRequest,
val state: FSMContext
val context: MutableFSMContext,
internal val fsmStrategy: FSMStrategy,
internal val enableApiStorage: Boolean
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import com.github.alice.ktx.models.button.Button
import com.github.alice.ktx.models.card.Card
import com.github.alice.ktx.models.request.AccountLinking
import com.github.alice.ktx.models.response.analytics.Analytics
import com.github.alice.ktx.models.response.analytics.AnalyticsEvent
import com.github.alice.ktx.state.FSMContext
import com.github.alice.ktx.state.ReadOnlyFSMContext
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

fun Request.response(body: MessageResponse.Builder.() -> Unit): MessageResponse {
return MessageResponse.Builder(this).build(body)
suspend fun Request.response(body: suspend MessageResponse.Builder.() -> Unit): MessageResponse {
return MessageResponse.Builder(this)
.setFSMStrategy(fsmStrategy)
.setEnableApiStorage(enableApiStorage)
.build(body)
}

/**
Expand All @@ -23,10 +25,10 @@ suspend fun Request.authorization(
onAlreadyAuthenticated: (suspend () -> MessageResponse)? = null,
onAuthorizationFailed: (suspend () -> MessageResponse)? = null
): MessageResponse {
if(message.session.user?.accessToken != null && onAlreadyAuthenticated != null)
if (message.session.user?.accessToken != null && onAlreadyAuthenticated != null)
return onAlreadyAuthenticated()

if(message.meta.interfaces.accountLinking == null && onAuthorizationFailed != null)
if (message.meta.interfaces.accountLinking == null && onAuthorizationFailed != null)
return onAuthorizationFailed()

return MessageResponse.AuthorizationBuilder(request = this).build()
Expand Down Expand Up @@ -59,12 +61,24 @@ data class MessageResponse internal constructor(
internal var audioPlayer: AudioPlayer? = null
private val buttons = mutableListOf<Button>()
var analytics: Analytics? = null
private var fSMStrategy = FSMStrategy.USER
private var enableApiStorage = false

internal fun addButton(button: Button) {
buttons.add(button)
}

internal fun build(body: Builder.() -> Unit): MessageResponse {
internal fun setFSMStrategy(strategy: FSMStrategy): Builder {
fSMStrategy = strategy
return this
}

internal fun setEnableApiStorage(enable: Boolean): Builder {
this.enableApiStorage = enable
return this
}

internal suspend fun build(body: suspend Builder.() -> Unit): MessageResponse {
body()

val response = MessageResponse(
Expand All @@ -83,7 +97,7 @@ data class MessageResponse internal constructor(
analytics = analytics
)

response.setState(request.state)
if (enableApiStorage) response.setState(fSMStrategy, request.context)

return response
}
Expand All @@ -101,9 +115,9 @@ data class MessageResponse internal constructor(
}
}

private fun setState(state: FSMContext) {
private suspend fun setState(fSMStrategy: FSMStrategy, state: ReadOnlyFSMContext) {
val stateResponse = getStateResponse(state.getState(), state.getData())
when (state.getStrategy()) {
when (fSMStrategy) {
FSMStrategy.USER -> userState = stateResponse
FSMStrategy.SESSION -> sessionState = stateResponse
FSMStrategy.APPLICATION -> applicationState = stateResponse
Expand Down
46 changes: 2 additions & 44 deletions alice-ktx/src/main/kotlin/com/github/alice/ktx/state/FSMContext.kt
Original file line number Diff line number Diff line change
@@ -1,52 +1,10 @@
package com.github.alice.ktx.state

import kotlin.reflect.KClass

/**
* Интерфейс для доступа к информации состояния конкретного пользователя.
* Создаётся и передаётся в обработчики при каждом событии.
* */
interface FSMContext: ReadOnlyFSMContext {

/**
* Установить состояние по ключу.
* */
fun setState(key: String)

/**
* Записать данные (перезапись) с произвольным типом.
* */
fun <V: Any> setTypedData(vararg pairs: Pair<String, V>, clazz: KClass<V>)

/**
* Записать данные (перезапись).
* */
fun setData(vararg pairs: Pair<String, String>)

/**
* Обновление данных в хранилище по ключу с произвольным типом.
* @return Обновленные данные.
* */
fun <V: Any> updateTypedData(vararg pairs: Pair<String, V>, clazz: KClass<V>): Map<String, String>

/**
* Обновление данные в хранилище по ключу.
* @return Обновленные данные.
* */
fun updateData(vararg pairs: Pair<String, String>): Map<String, String>

/**
* Удалить данные по ключу.
* */
fun removeData(key: String): String?

/**
* Удалить данные по ключу с произвольным типом.
* */
fun <V: Any> removeTypedData(key: String, clazz: KClass<V>): V?
interface FSMContext: MutableFSMContext {

/**
* Очистить состояние и данные.
* */
fun clear()
suspend fun init()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.github.alice.ktx.state

import kotlin.reflect.KClass

interface MutableFSMContext : ReadOnlyFSMContext {

/**
* Установить состояние по ключу.
* */
suspend fun setState(key: String)

/**
* Записать данные (перезапись) с произвольным типом.
* */
suspend fun <V: Any> setTypedData(vararg pairs: Pair<String, V>, clazz: KClass<V>)

/**
* Записать данные (перезапись).
* */
suspend fun setData(vararg pairs: Pair<String, String>)

/**
* Обновление данных в хранилище по ключу с произвольным типом.
* @return Обновленные данные.
* */
suspend fun <V: Any> updateTypedData(vararg pairs: Pair<String, V>, clazz: KClass<V>): Map<String, String>

/**
* Обновление данные в хранилище по ключу.
* @return Обновленные данные.
* */
suspend fun updateData(vararg pairs: Pair<String, String>): Map<String, String>

/**
* Удалить данные по ключу.
* */
suspend fun removeData(key: String): String?

/**
* Удалить данные по ключу с произвольным типом.
* */
suspend fun <V: Any> removeTypedData(key: String, clazz: KClass<V>): V?

/**
* Очистить состояние и данные.
* */
suspend fun clear()
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,26 @@
package com.github.alice.ktx.state

import com.github.alice.ktx.models.FSMStrategy
import kotlin.reflect.KClass

interface ReadOnlyFSMContext {

/**
* Получить стратегию FSM.
* */
fun getStrategy(): FSMStrategy

/**
* Получить текущее состояние.
* */
fun getState(): String?
suspend fun getState(): String?

/**
* Получить все данные.
* */
fun getData(): Map<String, String>
suspend fun getData(): Map<String, String>

/**
* Получить данные по ключу с произвольным типом.
* */
fun <V: Any> getTypedData(key: String, clazz: KClass<V>): V?
suspend fun <V: Any> getTypedData(key: String, clazz: KClass<V>): V?

/**
* Получить данные по ключу.
* */
fun getData(key: String): String?
suspend fun getData(key: String): String?
}
Loading

0 comments on commit d86f387

Please sign in to comment.