From bf1916c7c97a2c42ff8d4c7c93f12c7374ca3723 Mon Sep 17 00:00:00 2001 From: Sergey Okatov Date: Sat, 12 Oct 2024 13:04:50 +0300 Subject: [PATCH] M8l2 state machine (#32) --- .../main/kotlin/BuildPluginMultiplatform.kt | 1 - build.gradle.kts | 7 +- deploy/docker-compose.yml | 4 +- gradle.properties | 2 +- gradle/libs.versions.toml | 10 +++ ok-marketplace-be/gradle.properties | 2 +- .../ok-marketplace-app-kafka/build.gradle.kts | 2 +- .../ok-marketplace-app-ktor/build.gradle.kts | 17 +++-- .../kotlin/plugins/InitAppSettings.kt | 9 ++- .../build.gradle.kts | 2 +- .../build.gradle.kts | 6 +- .../ok-marketplace-biz/build.gradle.kts | 3 +- .../src/commonMain/kotlin/MkplAdProcessor.kt | 9 ++- .../commonMain/kotlin/general/GetAdState.kt | 17 +++++ .../commonMain/kotlin/general/GetAdStates.kt | 17 +++++ .../ok-marketplace-common/build.gradle.kts | 2 +- .../src/commonMain/kotlin/MkplContext.kt | 4 ++ .../src/commonMain/kotlin/MkplCorSettings.kt | 2 + .../src/commonMain/kotlin/models/MkplAd.kt | 6 +- ok-marketplace-states/.gitignore | 8 +++ ok-marketplace-states/build.gradle.kts | 37 +++++++++++ ok-marketplace-states/gradle.properties | 6 ++ .../build.gradle.kts | 38 +++++++++++ .../kotlin/MkplGetStateProcessor.kt | 44 +++++++++++++ .../kotlin/MkplUpdateStateProcessor.kt | 55 ++++++++++++++++ .../kotlin/general/ComputeAdState.kt | 39 +++++++++++ .../src/commonMain/kotlin/general/InitRepo.kt | 12 ++++ .../kotlin/general/PrepareResponse.kt | 12 ++++ .../kotlin/general/ReadStateFromDb.kt | 12 ++++ .../commonMain/kotlin/helper/InitStatus.kt | 15 +++++ .../resolver/SMAdStateResolverDefault.kt | 65 +++++++++++++++++++ .../src/commonMain/kotlin/stubs/StubNoCase.kt | 26 ++++++++ .../commonMain/kotlin/stubs/StubSuccess.kt | 31 +++++++++ .../src/commonMain/kotlin/stubs/Stubs.kt | 13 ++++ .../kotlin/validation/FinishValidation.kt | 14 ++++ .../kotlin/validation/ValidateIdNotEmpty.kt | 21 ++++++ .../validation/ValidateIdProperFormat.kt | 28 ++++++++ .../kotlin/validation/Validation.kt | 13 ++++ .../src/commonTest/kotlin/SMAdStateBizTest.kt | 48 ++++++++++++++ .../src/commonTest/kotlin/SMAdStateTest.kt | 38 +++++++++++ .../build.gradle.kts | 26 ++++++++ .../src/commonMain/kotlin/Constants.kt | 7 ++ .../commonMain/kotlin/IMkplStateContext.kt | 15 +++++ .../commonMain/kotlin/MkplAllStatesContext.kt | 20 ++++++ .../src/commonMain/kotlin/MkplStateContext.kt | 24 +++++++ .../kotlin/MkplStatesCorSettings.kt | 14 ++++ .../kotlin/helpers/MkplErrorsHelpers.kt | 48 ++++++++++++++ .../commonMain/kotlin/models/MkplAdStateId.kt | 12 ++++ .../src/commonMain/kotlin/models/MkplError.kt | 12 ++++ .../src/commonMain/kotlin/models/MkplState.kt | 8 +++ .../commonMain/kotlin/models/MkplStateRq.kt | 16 +++++ .../src/commonMain/kotlin/models/MkplStubs.kt | 6 ++ .../commonMain/kotlin/models/MkplWorkMode.kt | 7 ++ .../kotlin/statemachine/ISMAdStateResolver.kt | 11 ++++ .../kotlin/statemachine/SMAdSignal.kt | 9 +++ .../kotlin/statemachine/SMAdStates.kt | 16 +++++ .../kotlin/statemachine/SMTransition.kt | 11 ++++ ok-marketplace-states/settings.gradle.kts | 32 +++++++++ ok-marketplace-tests/gradle.properties | 1 + .../ok-marketplace-e2e-be/build.gradle.kts | 2 +- ok-marketplace-tests/settings.gradle.kts | 2 - settings.gradle.kts | 3 +- 62 files changed, 975 insertions(+), 24 deletions(-) create mode 100644 ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/general/GetAdState.kt create mode 100644 ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/general/GetAdStates.kt create mode 100644 ok-marketplace-states/.gitignore create mode 100644 ok-marketplace-states/build.gradle.kts create mode 100644 ok-marketplace-states/gradle.properties create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/build.gradle.kts create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/MkplGetStateProcessor.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/MkplUpdateStateProcessor.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/ComputeAdState.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/InitRepo.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/PrepareResponse.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/ReadStateFromDb.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/helper/InitStatus.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/resolver/SMAdStateResolverDefault.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/StubNoCase.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/StubSuccess.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/Stubs.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/FinishValidation.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/ValidateIdNotEmpty.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/ValidateIdProperFormat.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/Validation.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonTest/kotlin/SMAdStateBizTest.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-biz/src/commonTest/kotlin/SMAdStateTest.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/build.gradle.kts create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/Constants.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/IMkplStateContext.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplAllStatesContext.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplStateContext.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplStatesCorSettings.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/helpers/MkplErrorsHelpers.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplAdStateId.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplError.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplState.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplStateRq.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplStubs.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplWorkMode.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/ISMAdStateResolver.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMAdSignal.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMAdStates.kt create mode 100644 ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMTransition.kt create mode 100644 ok-marketplace-states/settings.gradle.kts diff --git a/build-plugin/src/main/kotlin/BuildPluginMultiplatform.kt b/build-plugin/src/main/kotlin/BuildPluginMultiplatform.kt index cd67784..f862b43 100644 --- a/build-plugin/src/main/kotlin/BuildPluginMultiplatform.kt +++ b/build-plugin/src/main/kotlin/BuildPluginMultiplatform.kt @@ -24,7 +24,6 @@ internal class BuildPluginMultiplatform : Plugin { configureTargets(this@with) sourceSets.configureEach { languageSettings.apply { - languageVersion = "1.9" progressiveMode = true optIn("kotlin.time.ExperimentalTime") } diff --git a/build.gradle.kts b/build.gradle.kts index f869aae..aa067a1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,6 +19,12 @@ subprojects { } tasks { + create("clean") { + group = "build" + gradle.includedBuilds.forEach { + dependsOn(it.task(":clean")) + } + } val buildImages: Task by creating { dependsOn(gradle.includedBuild("ok-marketplace-be").task(":buildImages")) } @@ -29,7 +35,6 @@ tasks { create("check") { group = "verification" -// dependsOn(gradle.includedBuild("ok-marketplace-be").task(":check")) dependsOn(buildImages) dependsOn(e2eTests) } diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 645fca6..20e06d5 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -77,8 +77,8 @@ services: nofile: soft: 65536 hard: 65536 - volumes: - - opensearch-data:/usr/share/opensearch/data +# volumes: +# - opensearch-data:/usr/share/opensearch/data dashboards: image: opensearchproject/opensearch-dashboards:latest diff --git a/gradle.properties b/gradle.properties index 20412e8..64fa36d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ kotlin.code.style=official -org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1024m -Dfile.encoding=UTF-8 kotlin.mpp.enableCInteropCommonization=true kotlin.native.ignoreDisabledTargets=true #kotlin.native.cacheKind.linuxX64=none diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9921cff..3718ab1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] kotlin = "2.0.0" +mkpl = "0.0.1" kotlinx-datetime = "0.5.0" kotlinx-serialization = "1.6.3" @@ -123,6 +124,15 @@ testcontainers-rabbitmq = { module = "org.testcontainers:rabbitmq", version.ref testcontainers-postgres = { module = "org.testcontainers:postgresql", version.ref = "testcontainers" } testcontainers-cassandra = { module = "org.testcontainers:cassandra", version.ref = "testcontainers" } +mkpl-logs-common = { module = "ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-common", version.ref = "mkpl" } +mkpl-logs-kermit = { module = "ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-kermit", version.ref = "mkpl" } +mkpl-logs-logback = { module = "ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-logback", version.ref = "mkpl" } +mkpl-logs-socket = { module = "ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-socket", version.ref = "mkpl" } +mkpl-cor = { module = "ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-cor", version.ref = "mkpl" } +mkpl-state-common = { module = "ru.otus.otuskotlin.marketplace.state:ok-marketplace-states-common", version.ref = "mkpl" } +mkpl-state-biz = { module = "ru.otus.otuskotlin.marketplace.state:ok-marketplace-states-biz", version.ref = "mkpl" } + + [bundles] kotest = ["kotest-junit5", "kotest-core", "kotest-datatest", "kotest-property"] exposed = ["db-exposed-core", "db-exposed-dao", "db-exposed-jdbc"] diff --git a/ok-marketplace-be/gradle.properties b/ok-marketplace-be/gradle.properties index 541bd1f..e1e62d5 100644 --- a/ok-marketplace-be/gradle.properties +++ b/ok-marketplace-be/gradle.properties @@ -1,6 +1,6 @@ kotlin.code.style=official kotlin.native.ignoreDisabledTargets=true kotlin.native.cacheKind.linuxX64=none -org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx3g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 diff --git a/ok-marketplace-be/ok-marketplace-app-kafka/build.gradle.kts b/ok-marketplace-be/ok-marketplace-app-kafka/build.gradle.kts index 8320558..e70a068 100644 --- a/ok-marketplace-be/ok-marketplace-app-kafka/build.gradle.kts +++ b/ok-marketplace-be/ok-marketplace-app-kafka/build.gradle.kts @@ -19,7 +19,7 @@ dependencies { implementation(libs.coroutines.core) implementation(libs.kotlinx.atomicfu) - implementation("ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-logback") + implementation(libs.mkpl.logs.logback) implementation(project(":ok-marketplace-app-common")) diff --git a/ok-marketplace-be/ok-marketplace-app-ktor/build.gradle.kts b/ok-marketplace-be/ok-marketplace-app-ktor/build.gradle.kts index ac27c10..3570fd6 100644 --- a/ok-marketplace-be/ok-marketplace-app-ktor/build.gradle.kts +++ b/ok-marketplace-be/ok-marketplace-app-ktor/build.gradle.kts @@ -81,11 +81,15 @@ kotlin { implementation(projects.okMarketplaceRepoInmemory) implementation(projects.okMarketplaceRepoPostgres) + // States + implementation(libs.mkpl.state.common) + implementation(libs.mkpl.state.biz) + // logging implementation(project(":ok-marketplace-api-log1")) - implementation("ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-common") - implementation("ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-kermit") - implementation("ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-socket") + implementation(libs.mkpl.logs.common) + implementation(libs.mkpl.logs.kermit) + implementation(libs.mkpl.logs.socket) } } @@ -115,13 +119,14 @@ kotlin { implementation(libs.logback) // transport models - implementation(project(":ok-marketplace-api-v1-jackson")) - implementation(project(":ok-marketplace-api-v1-mappers")) + implementation(projects.okMarketplaceApiV1Jackson) + implementation(projects.okMarketplaceApiV1Mappers) + implementation(projects.okMarketplaceApiV2Kmp) implementation(projects.okMarketplaceRepoCassandra) implementation(projects.okMarketplaceRepoGremlin) - implementation("ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-logback") + implementation(libs.mkpl.logs.logback) implementation(libs.testcontainers.cassandra) implementation(libs.testcontainers.core) } diff --git a/ok-marketplace-be/ok-marketplace-app-ktor/src/commonMain/kotlin/plugins/InitAppSettings.kt b/ok-marketplace-be/ok-marketplace-app-ktor/src/commonMain/kotlin/plugins/InitAppSettings.kt index 70f10be..998986a 100644 --- a/ok-marketplace-be/ok-marketplace-app-ktor/src/commonMain/kotlin/plugins/InitAppSettings.kt +++ b/ok-marketplace-be/ok-marketplace-app-ktor/src/commonMain/kotlin/plugins/InitAppSettings.kt @@ -5,15 +5,22 @@ import ru.otus.otuskotlin.marketplace.app.ktor.MkplAppSettings import ru.otus.otuskotlin.marketplace.app.ktor.base.KtorWsSessionRepo import ru.otus.otuskotlin.marketplace.backend.repository.inmemory.AdRepoStub import ru.otus.otuskotlin.marketplace.biz.MkplAdProcessor +import ru.otus.otuskotlin.marketplace.biz.statemachine.resolver.SMAdStateResolverDefault import ru.otus.otuskotlin.marketplace.common.MkplCorSettings +import ru.otus.otuskotlin.marketplace.states.common.MkplStatesCorSettings fun Application.initAppSettings(): MkplAppSettings { + val loggerProvider = getLoggerProviderConf() val corSettings = MkplCorSettings( - loggerProvider = getLoggerProviderConf(), + loggerProvider = loggerProvider, wsSessions = KtorWsSessionRepo(), repoTest = getDatabaseConf(AdDbType.TEST), repoProd = getDatabaseConf(AdDbType.PROD), repoStub = AdRepoStub(), + stateSettings = MkplStatesCorSettings( + loggerProvider = loggerProvider, + stateMachine = SMAdStateResolverDefault(), + ), ) return MkplAppSettings( appUrls = environment.config.propertyOrNull("ktor.urls")?.getList() ?: emptyList(), diff --git a/ok-marketplace-be/ok-marketplace-app-rabbit/build.gradle.kts b/ok-marketplace-be/ok-marketplace-app-rabbit/build.gradle.kts index deb66c9..3fd4f7a 100644 --- a/ok-marketplace-be/ok-marketplace-app-rabbit/build.gradle.kts +++ b/ok-marketplace-be/ok-marketplace-app-rabbit/build.gradle.kts @@ -20,7 +20,7 @@ dependencies { implementation(project(":ok-marketplace-common")) implementation(project(":ok-marketplace-app-common")) - implementation("ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-logback") + implementation(libs.mkpl.logs.logback) // v1 api implementation(project(":ok-marketplace-api-v1-jackson")) diff --git a/ok-marketplace-be/ok-marketplace-app-spring/build.gradle.kts b/ok-marketplace-be/ok-marketplace-app-spring/build.gradle.kts index 7f316a6..7139d5f 100644 --- a/ok-marketplace-be/ok-marketplace-app-spring/build.gradle.kts +++ b/ok-marketplace-be/ok-marketplace-app-spring/build.gradle.kts @@ -23,7 +23,7 @@ dependencies { // Внутренние модели implementation(project(":ok-marketplace-common")) implementation(project(":ok-marketplace-app-common")) - implementation("ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-logback") + implementation(libs.mkpl.logs.logback) // v1 api implementation(project(":ok-marketplace-api-v1-jackson")) @@ -42,6 +42,10 @@ dependencies { testImplementation(projects.okMarketplaceRepoCommon) testImplementation(projects.okMarketplaceStubs) + // State + implementation(libs.mkpl.state.common) + implementation(libs.mkpl.state.biz) + // tests testImplementation(kotlin("test-junit5")) testImplementation(libs.spring.test) diff --git a/ok-marketplace-be/ok-marketplace-biz/build.gradle.kts b/ok-marketplace-be/ok-marketplace-biz/build.gradle.kts index d29ec4c..ab4ccef 100644 --- a/ok-marketplace-be/ok-marketplace-biz/build.gradle.kts +++ b/ok-marketplace-be/ok-marketplace-biz/build.gradle.kts @@ -10,7 +10,8 @@ kotlin { dependencies { implementation(kotlin("stdlib-common")) - implementation(libs.cor) + implementation(libs.mkpl.cor) + implementation(libs.mkpl.state.common) implementation(project(":ok-marketplace-common")) implementation(project(":ok-marketplace-stubs")) diff --git a/ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/MkplAdProcessor.kt b/ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/MkplAdProcessor.kt index b7fb0b4..90a7225 100644 --- a/ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/MkplAdProcessor.kt +++ b/ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/MkplAdProcessor.kt @@ -1,5 +1,7 @@ package ru.otus.otuskotlin.marketplace.biz +import ru.otus.otuskotlin.marketplace.biz.general.getAdState +import ru.otus.otuskotlin.marketplace.biz.general.getAdStates import ru.otus.otuskotlin.marketplace.biz.general.initStatus import ru.otus.otuskotlin.marketplace.biz.general.operation import ru.otus.otuskotlin.marketplace.biz.repo.* @@ -49,6 +51,7 @@ class MkplAdProcessor( repoPrepareCreate("Подготовка объекта для сохранения") repoCreate("Создание объявления в БД") } + getAdState("Вычисление состояния объявления") prepareResult("Подготовка ответа") } operation("Получить объявление", MkplCommand.READ) { @@ -75,6 +78,7 @@ class MkplAdProcessor( handle { adRepoDone = adRepoRead } } } + getAdState("Вычисление состояния объявления") prepareResult("Подготовка ответа") } operation("Изменить объявление", MkplCommand.UPDATE) { @@ -110,6 +114,7 @@ class MkplAdProcessor( repoPrepareUpdate("Подготовка объекта для обновления") repoUpdate("Обновление объявления в БД") } + getAdState("Вычисление состояния объявления") prepareResult("Подготовка ответа") } operation("Удалить объявление", MkplCommand.DELETE) { @@ -138,6 +143,7 @@ class MkplAdProcessor( repoPrepareDelete("Подготовка объекта для удаления") repoDelete("Удаление объявления из БД") } + getAdState("Вычисление состояния объявления") prepareResult("Подготовка ответа") } operation("Поиск объявлений", MkplCommand.SEARCH) { @@ -154,6 +160,7 @@ class MkplAdProcessor( finishAdFilterValidation("Успешное завершение процедуры валидации") } repoSearch("Поиск объявления в БД по фильтру") + getAdStates("Вычисление состояния объявления") prepareResult("Подготовка ответа") } operation("Поиск подходящих предложений для объявления", MkplCommand.OFFERS) { @@ -177,8 +184,8 @@ class MkplAdProcessor( repoPrepareOffers("Подготовка данных для поиска предложений") repoOffers("Поиск предложений для объявления в БД") } + getAdStates("Вычисление состояния объявления") prepareResult("Подготовка ответа") } }.build() } - diff --git a/ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/general/GetAdState.kt b/ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/general/GetAdState.kt new file mode 100644 index 0000000..476d6ad --- /dev/null +++ b/ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/general/GetAdState.kt @@ -0,0 +1,17 @@ +package ru.otus.otuskotlin.marketplace.biz.general + +import ru.otus.otuskotlin.marketplace.common.MkplContext +import ru.otus.otuskotlin.marketplace.common.models.MkplState +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker + +fun ICorChainDsl.getAdState(title: String) = worker { + this.title = title + this.description = """ + Получаем состояние из сервиса состояний + """.trimIndent() + on { state == MkplState.RUNNING } + handle { + corSettings.stateSettings.stateMachine + } +} diff --git a/ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/general/GetAdStates.kt b/ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/general/GetAdStates.kt new file mode 100644 index 0000000..4390d2b --- /dev/null +++ b/ok-marketplace-be/ok-marketplace-biz/src/commonMain/kotlin/general/GetAdStates.kt @@ -0,0 +1,17 @@ +package ru.otus.otuskotlin.marketplace.biz.general + +import ru.otus.otuskotlin.marketplace.common.MkplContext +import ru.otus.otuskotlin.marketplace.common.models.MkplState +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker + +fun ICorChainDsl.getAdStates(title: String) = worker { + this.title = title + this.description = """ + Получаем состояние из сервиса состояний + """.trimIndent() + on { state == MkplState.RUNNING } + handle { + corSettings.stateSettings.stateMachine + } +} diff --git a/ok-marketplace-be/ok-marketplace-common/build.gradle.kts b/ok-marketplace-be/ok-marketplace-common/build.gradle.kts index 9b8d8d1..c103ffa 100644 --- a/ok-marketplace-be/ok-marketplace-common/build.gradle.kts +++ b/ok-marketplace-be/ok-marketplace-common/build.gradle.kts @@ -13,7 +13,7 @@ kotlin { api(libs.kotlinx.datetime) implementation(libs.coroutines.core) - api("ru.otus.otuskotlin.marketplace.libs:ok-marketplace-lib-logging-common") + api(libs.mkpl.state.common) } } val commonTest by getting { diff --git a/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/MkplContext.kt b/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/MkplContext.kt index 7403212..1520a0d 100644 --- a/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/MkplContext.kt +++ b/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/MkplContext.kt @@ -5,6 +5,7 @@ import ru.otus.otuskotlin.marketplace.common.models.* import ru.otus.otuskotlin.marketplace.common.repo.IRepoAd import ru.otus.otuskotlin.marketplace.common.stubs.MkplStubs import ru.otus.otuskotlin.marketplace.common.ws.IMkplWsSession +import ru.otus.otuskotlin.marketplace.states.common.models.MkplStateRq data class MkplContext( var command: MkplCommand = MkplCommand.NONE, @@ -33,6 +34,9 @@ data class MkplContext( var adRepoDone: MkplAd = MkplAd(), // Результат, полученный из БД var adsRepoDone: MutableList = mutableListOf(), + // Запрашиваем статус из модуля статистики + var adState: MkplStateRq = MkplStateRq(), + var adResponse: MkplAd = MkplAd(), var adsResponse: MutableList = mutableListOf(), ) diff --git a/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/MkplCorSettings.kt b/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/MkplCorSettings.kt index 4ca406e..2127ade 100644 --- a/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/MkplCorSettings.kt +++ b/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/MkplCorSettings.kt @@ -3,6 +3,7 @@ package ru.otus.otuskotlin.marketplace.common import ru.otus.otuskotlin.marketplace.common.repo.IRepoAd import ru.otus.otuskotlin.marketplace.common.ws.IMkplWsSessionRepo import ru.otus.otuskotlin.marketplace.logging.common.MpLoggerProvider +import ru.otus.otuskotlin.marketplace.states.common.MkplStatesCorSettings data class MkplCorSettings( val loggerProvider: MpLoggerProvider = MpLoggerProvider(), @@ -10,6 +11,7 @@ data class MkplCorSettings( val repoStub: IRepoAd = IRepoAd.NONE, val repoTest: IRepoAd = IRepoAd.NONE, val repoProd: IRepoAd = IRepoAd.NONE, + val stateSettings: MkplStatesCorSettings = MkplStatesCorSettings(), ) { companion object { val NONE = MkplCorSettings() diff --git a/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/models/MkplAd.kt b/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/models/MkplAd.kt index dfd5e36..9dc0efe 100644 --- a/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/models/MkplAd.kt +++ b/ok-marketplace-be/ok-marketplace-common/src/commonMain/kotlin/models/MkplAd.kt @@ -1,5 +1,7 @@ package ru.otus.otuskotlin.marketplace.common.models +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMAdStates + data class MkplAd( var id: MkplAdId = MkplAdId.NONE, var title: String = "", @@ -9,7 +11,9 @@ data class MkplAd( var visibility: MkplVisibility = MkplVisibility.NONE, var productId: MkplProductId = MkplProductId.NONE, var lock: MkplAdLock = MkplAdLock.NONE, - val permissionsClient: MutableSet = mutableSetOf() + val permissionsClient: MutableSet = mutableSetOf(), + + var adState: SMAdStates = SMAdStates.NONE, ) { fun deepCopy(): MkplAd = copy( permissionsClient = permissionsClient.toMutableSet(), diff --git a/ok-marketplace-states/.gitignore b/ok-marketplace-states/.gitignore new file mode 100644 index 0000000..d635568 --- /dev/null +++ b/ok-marketplace-states/.gitignore @@ -0,0 +1,8 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +/.idea/ +/kotlin-js-store/ diff --git a/ok-marketplace-states/build.gradle.kts b/ok-marketplace-states/build.gradle.kts new file mode 100644 index 0000000..abbfd5c --- /dev/null +++ b/ok-marketplace-states/build.gradle.kts @@ -0,0 +1,37 @@ +plugins { + alias(libs.plugins.kotlin.jvm) apply false + alias(libs.plugins.kotlin.multiplatform) apply false + alias(libs.plugins.kotlin.kapt) apply false + alias(libs.plugins.muschko.remote) apply false + alias(libs.plugins.muschko.java) apply false +} + +group = "ru.otus.otuskotlin.marketplace.state" +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-ad-v1.yaml").toString()) + set("spec-v2", specDir.file("specs-ad-v2.yaml").toString()) + set("spec-log1", specDir.file("specs-ad-log1.yaml").toString()) +} + +tasks { + arrayOf("build", "clean", "check").forEach {tsk -> + create(tsk) { + group = "build" + dependsOn(subprojects.map { it.getTasksByName(tsk,false)}) + } + } +} diff --git a/ok-marketplace-states/gradle.properties b/ok-marketplace-states/gradle.properties new file mode 100644 index 0000000..bfb357d --- /dev/null +++ b/ok-marketplace-states/gradle.properties @@ -0,0 +1,6 @@ +kotlin.code.style=official +kotlin.native.ignoreDisabledTargets=true +kotlin.native.cacheKind.linuxX64=none +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1024m -Dfile.encoding=UTF-8 + + diff --git a/ok-marketplace-states/ok-marketplace-states-biz/build.gradle.kts b/ok-marketplace-states/ok-marketplace-states-biz/build.gradle.kts new file mode 100644 index 0000000..0d70723 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/build.gradle.kts @@ -0,0 +1,38 @@ +plugins { + id("build-kmp") +} + +kotlin { + sourceSets { + all { languageSettings.optIn("kotlin.RequiresOptIn") } + + commonMain { + dependencies { + implementation(kotlin("stdlib-common")) + + implementation(libs.mkpl.cor) + implementation(libs.coroutines.core) + + implementation(projects.okMarketplaceStatesCommon) + } + } + commonTest { + dependencies { + implementation(kotlin("test-common")) + implementation(kotlin("test-annotations-common")) + + api(libs.coroutines.test) + } + } + jvmMain { + dependencies { + implementation(kotlin("stdlib-jdk8")) + } + } + jvmTest { + dependencies { + implementation(kotlin("test-junit")) + } + } + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/MkplGetStateProcessor.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/MkplGetStateProcessor.kt new file mode 100644 index 0000000..fbdbb85 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/MkplGetStateProcessor.kt @@ -0,0 +1,44 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine + +import ru.otus.otuskotlin.marketplace.biz.statemachine.general.initRepo +import ru.otus.otuskotlin.marketplace.biz.statemachine.general.prepareResponse +import ru.otus.otuskotlin.marketplace.biz.statemachine.general.readStateFromDb +import ru.otus.otuskotlin.marketplace.biz.statemachine.helper.initStatus +import ru.otus.otuskotlin.marketplace.biz.statemachine.stubs.stubNoCase +import ru.otus.otuskotlin.marketplace.biz.statemachine.stubs.stubSuccess +import ru.otus.otuskotlin.marketplace.biz.statemachine.stubs.stubs +import ru.otus.otuskotlin.marketplace.biz.statemachine.validation.finishValidation +import ru.otus.otuskotlin.marketplace.biz.statemachine.validation.validateIdNotEmpty +import ru.otus.otuskotlin.marketplace.biz.statemachine.validation.validateIdProperFormat +import ru.otus.otuskotlin.marketplace.biz.statemachine.validation.validation +import ru.otus.otuskotlin.marketplace.cor.rootChain +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext +import ru.otus.otuskotlin.marketplace.states.common.MkplStatesCorSettings +import ru.otus.otuskotlin.marketplace.states.common.models.MkplAdStateId + +class MkplAdStateProcessor( + private val corSettings: MkplStatesCorSettings = MkplStatesCorSettings.NONE +) { + suspend fun exec(ctx: MkplStateContext) = businessChain.exec(ctx.also { it.corSettings = corSettings }) + + private val businessChain = rootChain { + initStatus("Инициализация статуса") + initRepo("Инициализация репозитория") + + stubs("Обработка стабов") { + stubSuccess("Успешный сценарий") + stubNoCase("Ошибка: запрошенный стаб недопустим") + } + validation { + worker("Копируем поля в adValidating") { rqValidating = stateRequest.deepCopy() } + worker("Очистка id") { rqValidating.adId = MkplAdStateId(rqValidating.adId.asString().trim()) } + validateIdNotEmpty("Проверка на непустой id") + validateIdProperFormat("Проверка формата id") + + finishValidation("Успешное завершение процедуры валидации") + } + readStateFromDb("Чтение состояния из БД") + prepareResponse("Подготовка ответа") + }.build() +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/MkplUpdateStateProcessor.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/MkplUpdateStateProcessor.kt new file mode 100644 index 0000000..a0de917 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/MkplUpdateStateProcessor.kt @@ -0,0 +1,55 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine + +import ru.otus.otuskotlin.marketplace.biz.statemachine.general.computeAdState +import ru.otus.otuskotlin.marketplace.biz.statemachine.general.initRepo +import ru.otus.otuskotlin.marketplace.biz.statemachine.helper.initStatus +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.rootChain +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.MkplAllStatesContext +import ru.otus.otuskotlin.marketplace.states.common.MkplStatesCorSettings + +class MkplUpdateStateProcessor( + private val corSettings: MkplStatesCorSettings = MkplStatesCorSettings.NONE +) { + suspend fun exec(ctx: MkplAllStatesContext) = businessChain.exec(ctx.also { it.corSettings = corSettings }) + + private val businessChain = rootChain { + initStatus("Инициализация статуса") + initRepo("Инициализация репозитория") + + readAllStatesWithLock("Чтение и блокировка всех объектов в БД на время вычисления") + requestStatisticsServer("Запрос сервера статистики") + computeAdState("Вычисление обновленного состояния") +// filterUpdated("Вычисляет объявления для сохранения") + updateStatesWithLock("Чтение и блокировка всех объектов в БД на время вычисления") + }.build() +} + +private fun ICorChainDsl.readAllStatesWithLock(title: String) { + this.title = title + this.description = """ + Запрашиваем все объекы из БД + Проверяем блокировку и время последнего обновления + - Если блокировка не установлена, ставим свою + - Если блокировка установлена давно, переписываем на свою + - Иначе пропускаем, она захвачена другим процессом + Прочитанные объекты направляем во flow + """.trimIndent() +} + +private fun ICorChainDsl.requestStatisticsServer(title: String) = worker { + this.title = title + this.description = """ + Выполняет батчевые запросы на сервер статистики (в мониторинг, OpenSearch) + """.trimIndent() +} + +private fun ICorChainDsl.updateStatesWithLock(title: String) = worker { + this.title = title + this.description = """ + Читает поток вычисленных обновлений и сохраняет их в БД с учетом блокировки. + Там, где блокировки изменилась, такие объекты пропускаем, их перехватил другой процесс + """.trimIndent() +} + diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/ComputeAdState.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/ComputeAdState.kt new file mode 100644 index 0000000..d871cca --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/ComputeAdState.kt @@ -0,0 +1,39 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.general + +import kotlinx.coroutines.flow.onEach +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.MkplAllStatesContext +import ru.otus.otuskotlin.marketplace.states.common.NONE +import ru.otus.otuskotlin.marketplace.states.common.models.MkplState +import ru.otus.otuskotlin.marketplace.states.common.models.MkplStateRq +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMAdSignal +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMAdStates +import kotlin.reflect.KClass + +private val clazz: KClass<*> = ICorChainDsl::computeAdState::class +fun ICorChainDsl.computeAdState(title: String) = worker { + this.title = title + this.description = "Вычисление состояния объявления" + on { state == MkplState.RUNNING } + handle { + val machine = this.corSettings.stateMachine + val log = corSettings.loggerProvider.logger(clazz) + statesComputed = statesWStat.onEach { sc: MkplStateRq -> + val timeNow = Clock.System.now() + val timePublished = sc.created.takeIf { it != Instant.NONE } ?: timeNow + val signal = SMAdSignal( + state = sc.oldState.takeIf { it != SMAdStates.NONE } ?: SMAdStates.NEW, + duration = timeNow - timePublished, + views = sc.views, + ) + val transition = machine.resolve(signal) + if (transition.state != sc.oldState) { + log.info("New ad state transition: ${transition.description}") + } + sc.transition = transition + } + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/InitRepo.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/InitRepo.kt new file mode 100644 index 0000000..8243f67 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/InitRepo.kt @@ -0,0 +1,12 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.general + +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.IMkplStateContext + +fun ICorChainDsl.initRepo(title: String) = worker { + this.title = title + this.description = """ + Вычисление актуального репозитория в зависимости от типа запроса + """.trimIndent() +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/PrepareResponse.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/PrepareResponse.kt new file mode 100644 index 0000000..08848a3 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/PrepareResponse.kt @@ -0,0 +1,12 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.general + +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext + +fun ICorChainDsl.prepareResponse(title: String) = worker { + this.title = title + this.description = """ + Подготовка ответа + """.trimIndent() +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/ReadStateFromDb.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/ReadStateFromDb.kt new file mode 100644 index 0000000..642ce41 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/general/ReadStateFromDb.kt @@ -0,0 +1,12 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.general + +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext + +fun ICorChainDsl.readStateFromDb(title: String) = worker { + this.title = title + this.description = """ + Чтение состояния из БД + """.trimIndent() +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/helper/InitStatus.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/helper/InitStatus.kt new file mode 100644 index 0000000..ca9bd8a --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/helper/InitStatus.kt @@ -0,0 +1,15 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.helper + +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.IMkplStateContext +import ru.otus.otuskotlin.marketplace.states.common.models.MkplState + +fun ICorChainDsl.initStatus(title: String) = worker() { + this.title = title + this.description = """ + Этот обработчик устанавливает стартовый статус обработки. Запускается только в случае не заданного статуса. + """.trimIndent() + on { state == MkplState.NONE } + handle { state = MkplState.RUNNING } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/resolver/SMAdStateResolverDefault.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/resolver/SMAdStateResolverDefault.kt new file mode 100644 index 0000000..4efd0ad --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/resolver/SMAdStateResolverDefault.kt @@ -0,0 +1,65 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.resolver + +import ru.otus.otuskotlin.marketplace.states.common.statemachine.ISMAdStateResolver +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMAdSignal +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMAdStates +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMTransition +import kotlin.time.Duration +import kotlin.time.Duration.Companion.days +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds + +class SMAdStateResolverDefault: ISMAdStateResolver { + override fun resolve(signal: SMAdSignal): SMTransition { + require(signal.duration >= 0.milliseconds) { "Publication duration cannot be negative" } + require(signal.views >= 0) { "View count cannot be negative" } + val sig = Sig( + st = signal.state, + dur = SMDurs.entries.first { signal.duration >= it.min && signal.duration < it.max }, + vws = SMViews.entries.first { signal.views >= it.min && signal.views < it.max }, + ) + + return TR_MX[sig] ?: SMTransition.ERROR + } + + companion object { + private enum class SMDurs(val min: Duration, val max: Duration) { + D_NEW(0.seconds, 3.days), + D_ACT(3.days, 14.days), + D_OLD(14.days, Int.MAX_VALUE.seconds), + } + + private enum class SMViews(val min: Int, val max: Int) { FEW(0, 30), MODER(30, 100), LARGE(100, Int.MAX_VALUE) } + private data class Sig( + val st: SMAdStates, + val dur: SMDurs, + val vws: SMViews, + ) + + private val TR_MX = mapOf( + Sig(SMAdStates.NEW, SMDurs.D_NEW, SMViews.FEW) to SMTransition(SMAdStates.NEW, "Новое без изменений"), + Sig(SMAdStates.NEW, SMDurs.D_ACT, SMViews.FEW) to SMTransition(SMAdStates.ACTUAL, "Вышло время, перевод из нового в актуальное"), + Sig(SMAdStates.NEW, SMDurs.D_NEW, SMViews.MODER) to SMTransition( + SMAdStates.HIT, + "Много просмотров, стало хитом" + ), + Sig(SMAdStates.NEW, SMDurs.D_NEW, SMViews.LARGE) to SMTransition( + SMAdStates.HIT, + "Очень много просмотров, стало хитом" + ), + Sig(SMAdStates.HIT, SMDurs.D_NEW, SMViews.MODER) to SMTransition(SMAdStates.HIT, "Остается хитом"), + Sig(SMAdStates.HIT, SMDurs.D_ACT, SMViews.MODER) to SMTransition( + SMAdStates.ACTUAL, + "Время вышло, хит утих, становится актуальным" + ), + Sig(SMAdStates.HIT, SMDurs.D_ACT, SMViews.LARGE) to SMTransition( + SMAdStates.ACTUAL, + "Время вышло, хит становится популярным" + ), + Sig(SMAdStates.NEW, SMDurs.D_OLD, SMViews.FEW) to SMTransition( + SMAdStates.OLD, + "Устарело, просмотров мало, непопулярное и старое объявление" + ), + ) + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/StubNoCase.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/StubNoCase.kt new file mode 100644 index 0000000..19114fc --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/StubNoCase.kt @@ -0,0 +1,26 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.stubs + +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext +import ru.otus.otuskotlin.marketplace.states.common.helpers.fail +import ru.otus.otuskotlin.marketplace.states.common.models.MkplError +import ru.otus.otuskotlin.marketplace.states.common.models.MkplState + +fun ICorChainDsl.stubNoCase(title: String) = worker { + this.title = title + this.description = """ + Валидируем ситуацию, когда запрошен кейс, который не поддерживается в стабах + """.trimIndent() + on { state == MkplState.RUNNING } + handle { + fail( + MkplError( + code = "validation", + field = "stub", + group = "validation", + message = "Wrong stub case is requested: ${stubCase.name}" + ) + ) + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/StubSuccess.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/StubSuccess.kt new file mode 100644 index 0000000..d747924 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/StubSuccess.kt @@ -0,0 +1,31 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.stubs + +import kotlinx.datetime.Clock +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext +import ru.otus.otuskotlin.marketplace.states.common.models.MkplAdStateId +import ru.otus.otuskotlin.marketplace.states.common.models.MkplState +import ru.otus.otuskotlin.marketplace.states.common.models.MkplStateRq +import ru.otus.otuskotlin.marketplace.states.common.models.MkplStubs +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMAdStates +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMTransition +import kotlin.time.Duration.Companion.days + +fun ICorChainDsl.stubSuccess(title: String) = worker { + this.title = title + this.description = """ + Обрабатываем сценарий стаба с успешным запросом, когда возвращается объект с состоянием + """.trimIndent() + on { stubCase == MkplStubs.SUCCESS && state == MkplState.RUNNING } + handle { + stateResponse = MkplStateRq( + adId = stateRequest.adId.takeIf { it != MkplAdStateId.NONE } ?: MkplAdStateId("123"), + oldState = SMAdStates.ACTUAL, + created = Clock.System.now() - 3.days, + views = 10, + transition = SMTransition.NONE + ) + state = MkplState.FINISHING + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/Stubs.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/Stubs.kt new file mode 100644 index 0000000..d268a09 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/stubs/Stubs.kt @@ -0,0 +1,13 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.stubs + +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.chain +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext +import ru.otus.otuskotlin.marketplace.states.common.models.MkplState +import ru.otus.otuskotlin.marketplace.states.common.models.MkplWorkMode + +fun ICorChainDsl.stubs(title: String, block: ICorChainDsl.() -> Unit) = chain { + block() + this.title = title + on { workMode == MkplWorkMode.STUB && state == MkplState.RUNNING } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/FinishValidation.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/FinishValidation.kt new file mode 100644 index 0000000..d163693 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/FinishValidation.kt @@ -0,0 +1,14 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.validation + +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext +import ru.otus.otuskotlin.marketplace.states.common.models.MkplState + +fun ICorChainDsl.finishValidation(title: String) = worker { + this.title = title + on { state == MkplState.RUNNING } + handle { + rqValidated = rqValidating + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/ValidateIdNotEmpty.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/ValidateIdNotEmpty.kt new file mode 100644 index 0000000..cfd46fa --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/ValidateIdNotEmpty.kt @@ -0,0 +1,21 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.validation + +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext +import ru.otus.otuskotlin.marketplace.states.common.helpers.errorValidation +import ru.otus.otuskotlin.marketplace.states.common.helpers.fail + +fun ICorChainDsl.validateIdNotEmpty(title: String) = worker { + this.title = title + on { rqValidating.adId.asString().isEmpty() } + handle { + fail( + errorValidation( + field = "id", + violationCode = "empty", + description = "field must not be empty" + ) + ) + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/ValidateIdProperFormat.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/ValidateIdProperFormat.kt new file mode 100644 index 0000000..eade781 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/ValidateIdProperFormat.kt @@ -0,0 +1,28 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.validation + +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.worker +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext +import ru.otus.otuskotlin.marketplace.states.common.helpers.errorValidation +import ru.otus.otuskotlin.marketplace.states.common.helpers.fail +import ru.otus.otuskotlin.marketplace.states.common.models.MkplAdStateId + +fun ICorChainDsl.validateIdProperFormat(title: String) = worker { + this.title = title + + // Может быть вынесен в MkplAdId для реализации различных форматов + val regExp = Regex("^[0-9a-zA-Z#:-]+$") + on { rqValidating.adId != MkplAdStateId.NONE && !rqValidating.adId.asString().matches(regExp) } + handle { + val encodedId = rqValidating.adId.asString() + .replace("<", "<") + .replace(">", ">") + fail( + errorValidation( + field = "id", + violationCode = "badFormat", + description = "value $encodedId must contain only letters and numbers" + ) + ) + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/Validation.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/Validation.kt new file mode 100644 index 0000000..e66ee90 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonMain/kotlin/validation/Validation.kt @@ -0,0 +1,13 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine.validation + +import ru.otus.otuskotlin.marketplace.cor.ICorChainDsl +import ru.otus.otuskotlin.marketplace.cor.chain +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext +import ru.otus.otuskotlin.marketplace.states.common.models.MkplState + +fun ICorChainDsl.validation(block: ICorChainDsl.() -> Unit) = chain { + block() + title = "Валидация" + + on { state == MkplState.RUNNING } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonTest/kotlin/SMAdStateBizTest.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonTest/kotlin/SMAdStateBizTest.kt new file mode 100644 index 0000000..e41bace --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonTest/kotlin/SMAdStateBizTest.kt @@ -0,0 +1,48 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine + +import kotlinx.coroutines.test.runTest +import ru.otus.otuskotlin.marketplace.biz.statemachine.resolver.SMAdStateResolverDefault +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext +import ru.otus.otuskotlin.marketplace.states.common.MkplStatesCorSettings +import ru.otus.otuskotlin.marketplace.states.common.models.* +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMAdSignal +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMAdStates +import kotlin.test.Test +import kotlin.test.assertContains +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals +import kotlin.time.Duration.Companion.days + +class SMAdStateBizTest { + + @Test + fun bizGetTest() = runTest { + val machine = SMAdStateResolverDefault() + val settings = MkplStatesCorSettings(stateMachine = machine) + val processor = MkplAdStateProcessor(corSettings = settings) + val ctx = MkplStateContext( + workMode = MkplWorkMode.STUB, + stubCase = MkplStubs.SUCCESS, + stateRequest = MkplStateRq( + adId = MkplAdStateId("some") + ) + ) + processor.exec(ctx) + assertEquals(SMAdStates.ACTUAL, ctx.stateResponse.oldState) + assertContentEquals(emptyList(), ctx.errors) + assertEquals(MkplState.FINISHING, ctx.state) + } + + @Test + fun new2hit() { + val machine = SMAdStateResolverDefault() + val signal = SMAdSignal( + state = SMAdStates.NEW, + duration = 2.days, + views = 101, + ) + val transition = machine.resolve(signal) + assertEquals(SMAdStates.HIT, transition.state) + assertContains(transition.description, "Очень", ignoreCase = true) + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-biz/src/commonTest/kotlin/SMAdStateTest.kt b/ok-marketplace-states/ok-marketplace-states-biz/src/commonTest/kotlin/SMAdStateTest.kt new file mode 100644 index 0000000..9d37dc7 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-biz/src/commonTest/kotlin/SMAdStateTest.kt @@ -0,0 +1,38 @@ +package ru.otus.otuskotlin.marketplace.biz.statemachine + +import ru.otus.otuskotlin.marketplace.biz.statemachine.resolver.SMAdStateResolverDefault +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMAdSignal +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMAdStates +import kotlin.test.Test +import kotlin.test.assertContains +import kotlin.test.assertEquals +import kotlin.time.Duration.Companion.days + +class SMAdStateTest { + + @Test + fun new2actual() { + val machine = SMAdStateResolverDefault() + val signal = SMAdSignal( + state = SMAdStates.NEW, + duration = 4.days, + views = 20, + ) + val transition = machine.resolve(signal) + assertEquals(SMAdStates.ACTUAL, transition.state) + assertContains(transition.description, "актуальное", ignoreCase = true) + } + + @Test + fun new2hit() { + val machine = SMAdStateResolverDefault() + val signal = SMAdSignal( + state = SMAdStates.NEW, + duration = 2.days, + views = 101, + ) + val transition = machine.resolve(signal) + assertEquals(SMAdStates.HIT, transition.state) + assertContains(transition.description, "Очень", ignoreCase = true) + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-common/build.gradle.kts b/ok-marketplace-states/ok-marketplace-states-common/build.gradle.kts new file mode 100644 index 0000000..e5fb186 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + id("build-kmp") +} + +group = rootProject.group +version = rootProject.version + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(kotlin("stdlib-common")) + + api(libs.kotlinx.datetime) + implementation(libs.coroutines.core) + api(libs.mkpl.logs.common) + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test-common")) + implementation(kotlin("test-annotations-common")) + } + } + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/Constants.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/Constants.kt new file mode 100644 index 0000000..b96b42b --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/Constants.kt @@ -0,0 +1,7 @@ +package ru.otus.otuskotlin.marketplace.states.common + +import kotlinx.datetime.Instant + +private val INSTANT_NONE = Instant.fromEpochMilliseconds(Long.MIN_VALUE) +val Instant.Companion.NONE + get() = INSTANT_NONE diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/IMkplStateContext.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/IMkplStateContext.kt new file mode 100644 index 0000000..2e9ab14 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/IMkplStateContext.kt @@ -0,0 +1,15 @@ +package ru.otus.otuskotlin.marketplace.states.common + +import kotlinx.datetime.Instant +import ru.otus.otuskotlin.marketplace.states.common.models.MkplError +import ru.otus.otuskotlin.marketplace.states.common.models.MkplState + +interface IMkplStateContext { + var state: MkplState + val errors: MutableList + + var corSettings: MkplStatesCorSettings + + var timeStart: Instant + +} diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplAllStatesContext.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplAllStatesContext.kt new file mode 100644 index 0000000..9711911 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplAllStatesContext.kt @@ -0,0 +1,20 @@ +package ru.otus.otuskotlin.marketplace.states.common + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.datetime.Instant +import ru.otus.otuskotlin.marketplace.states.common.models.* + +data class MkplAllStatesContext( + override var state: MkplState = MkplState.NONE, + override val errors: MutableList = mutableListOf(), + + override var corSettings: MkplStatesCorSettings = MkplStatesCorSettings(), + + override var timeStart: Instant = Instant.NONE, + + var statesRead: Flow = flowOf(), + var statesWStat: Flow = flowOf(), + var statesComputed: Flow = flowOf(), + var statesUpdating: Flow = flowOf(), +): IMkplStateContext diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplStateContext.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplStateContext.kt new file mode 100644 index 0000000..24f065c --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplStateContext.kt @@ -0,0 +1,24 @@ +package ru.otus.otuskotlin.marketplace.states.common + +import kotlinx.datetime.Instant +import ru.otus.otuskotlin.marketplace.states.common.models.* + +data class MkplStateContext( + override var state: MkplState = MkplState.NONE, + var workMode: MkplWorkMode = MkplWorkMode.PROD, + var stubCase: MkplStubs = MkplStubs.NONE, + override val errors: MutableList = mutableListOf(), + + override var corSettings: MkplStatesCorSettings = MkplStatesCorSettings(), + + override var timeStart: Instant = Instant.NONE, + + var stateRequest: MkplStateRq = MkplStateRq(), + var rqValidating: MkplStateRq = MkplStateRq(), + var rqValidated: MkplStateRq = MkplStateRq(), + + var stateRead: MkplStateRq = MkplStateRq(), + var stateWStats: MkplStateRq = MkplStateRq(), + + var stateResponse: MkplStateRq = MkplStateRq(), +): IMkplStateContext diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplStatesCorSettings.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplStatesCorSettings.kt new file mode 100644 index 0000000..734b866 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/MkplStatesCorSettings.kt @@ -0,0 +1,14 @@ +package ru.otus.otuskotlin.marketplace.states.common + +import ru.otus.otuskotlin.marketplace.logging.common.MpLoggerProvider +import ru.otus.otuskotlin.marketplace.states.common.statemachine.ISMAdStateResolver + +data class MkplStatesCorSettings( + val loggerProvider: MpLoggerProvider = MpLoggerProvider(), + val stateMachine: ISMAdStateResolver = ISMAdStateResolver.NONE +) { + companion object { + val NONE = MkplStatesCorSettings() + } +} + diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/helpers/MkplErrorsHelpers.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/helpers/MkplErrorsHelpers.kt new file mode 100644 index 0000000..1e8d7b6 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/helpers/MkplErrorsHelpers.kt @@ -0,0 +1,48 @@ +package ru.otus.otuskotlin.marketplace.states.common.helpers + +import ru.otus.otuskotlin.marketplace.logging.common.LogLevel +import ru.otus.otuskotlin.marketplace.states.common.MkplStateContext +import ru.otus.otuskotlin.marketplace.states.common.models.MkplError +import ru.otus.otuskotlin.marketplace.states.common.models.MkplState + +inline fun MkplStateContext.addError(error: MkplError) = errors.add(error) +inline fun MkplStateContext.addErrors(error: Collection) = errors.addAll(error) + +inline fun MkplStateContext.fail(error: MkplError) { + addError(error) + state = MkplState.FAILING +} + +inline fun MkplStateContext.fail(errors: Collection) { + addErrors(errors) + state = MkplState.FAILING +} + +inline fun errorValidation( + field: String, + /** + * Код, характеризующий ошибку. Не должен включать имя поля или указание на валидацию. + * Например: empty, badSymbols, tooLong, etc + */ + violationCode: String, + description: String, + level: LogLevel = LogLevel.ERROR, +) = MkplError( + code = "validation-$field-$violationCode", + field = field, + group = "validation", + message = "Validation error for field $field: $description", + level = level, +) + +inline fun errorSystem( + violationCode: String, + level: LogLevel = LogLevel.ERROR, + e: Throwable, +) = MkplError( + code = "system-$violationCode", + group = "system", + message = "System error occurred. Our stuff has been informed, please retry later", + level = level, + exception = e, +) diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplAdStateId.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplAdStateId.kt new file mode 100644 index 0000000..40c222e --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplAdStateId.kt @@ -0,0 +1,12 @@ +package ru.otus.otuskotlin.marketplace.states.common.models + +import kotlin.jvm.JvmInline + +@JvmInline +value class MkplAdStateId(private val id: String) { + fun asString() = id + + companion object { + val NONE = MkplAdStateId("") + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplError.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplError.kt new file mode 100644 index 0000000..9655a47 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplError.kt @@ -0,0 +1,12 @@ +package ru.otus.otuskotlin.marketplace.states.common.models + +import ru.otus.otuskotlin.marketplace.logging.common.LogLevel + +data class MkplError( + val code: String = "", + val group: String = "", + val field: String = "", + val message: String = "", + val level: LogLevel = LogLevel.ERROR, + val exception: Throwable? = null, +) diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplState.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplState.kt new file mode 100644 index 0000000..3c9b445 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplState.kt @@ -0,0 +1,8 @@ +package ru.otus.otuskotlin.marketplace.states.common.models + +enum class MkplState { + NONE, + RUNNING, + FAILING, + FINISHING, +} diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplStateRq.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplStateRq.kt new file mode 100644 index 0000000..1a17421 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplStateRq.kt @@ -0,0 +1,16 @@ +package ru.otus.otuskotlin.marketplace.states.common.models + +import kotlinx.datetime.Instant +import ru.otus.otuskotlin.marketplace.states.common.NONE +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMAdStates +import ru.otus.otuskotlin.marketplace.states.common.statemachine.SMTransition + +data class MkplStateRq( + var adId: MkplAdStateId = MkplAdStateId.NONE, + var oldState: SMAdStates = SMAdStates.NONE, + var created: Instant = Instant.NONE, + var views: Int = 0, + var transition: SMTransition = SMTransition.ERROR, +) { + fun deepCopy() = copy() +} diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplStubs.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplStubs.kt new file mode 100644 index 0000000..e5e3631 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplStubs.kt @@ -0,0 +1,6 @@ +package ru.otus.otuskotlin.marketplace.states.common.models + +enum class MkplStubs { + NONE, + SUCCESS, +} diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplWorkMode.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplWorkMode.kt new file mode 100644 index 0000000..d20a61b --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/models/MkplWorkMode.kt @@ -0,0 +1,7 @@ +package ru.otus.otuskotlin.marketplace.states.common.models + +enum class MkplWorkMode { + PROD, + TEST, + STUB, +} diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/ISMAdStateResolver.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/ISMAdStateResolver.kt new file mode 100644 index 0000000..e9a4a68 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/ISMAdStateResolver.kt @@ -0,0 +1,11 @@ +package ru.otus.otuskotlin.marketplace.states.common.statemachine + +interface ISMAdStateResolver { + fun resolve(signal: SMAdSignal): SMTransition + + companion object { + val NONE = object: ISMAdStateResolver { + override fun resolve(signal: SMAdSignal): SMTransition = SMTransition.ERROR + } + } +} diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMAdSignal.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMAdSignal.kt new file mode 100644 index 0000000..0e20fb5 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMAdSignal.kt @@ -0,0 +1,9 @@ +package ru.otus.otuskotlin.marketplace.states.common.statemachine + +import kotlin.time.Duration + +data class SMAdSignal( + val state: SMAdStates, + val duration: Duration, + val views: Int, +) diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMAdStates.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMAdStates.kt new file mode 100644 index 0000000..ce14968 --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMAdStates.kt @@ -0,0 +1,16 @@ +package ru.otus.otuskotlin.marketplace.states.common.statemachine + + +@Suppress("unused") +enum class SMAdStates { + NONE, // не инициализировано состояние + DRAFT, // черновик + NEW, // только что опубликовано + ACTUAL, // актуальное + OLD, // старое объявление + HIT, // новое объявление с большим числом просмотров + POPULAR, // актуальное объявление с большим числом просмотров + + ERROR, // ошибки вычисления; + +} diff --git a/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMTransition.kt b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMTransition.kt new file mode 100644 index 0000000..68bf6cf --- /dev/null +++ b/ok-marketplace-states/ok-marketplace-states-common/src/commonMain/kotlin/statemachine/SMTransition.kt @@ -0,0 +1,11 @@ +package ru.otus.otuskotlin.marketplace.states.common.statemachine + +data class SMTransition( + val state: SMAdStates, + val description: String, +) { + companion object { + val ERROR = SMTransition(SMAdStates.ERROR, "Unprovided transition occurred") + val NONE = SMTransition(SMAdStates.NONE, "Empty Transition") + } +} diff --git a/ok-marketplace-states/settings.gradle.kts b/ok-marketplace-states/settings.gradle.kts new file mode 100644 index 0000000..0a2e4f8 --- /dev/null +++ b/ok-marketplace-states/settings.gradle.kts @@ -0,0 +1,32 @@ +rootProject.name = "ok-marketplace-states" + +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +pluginManagement { + includeBuild("../build-plugin") + plugins { + id("build-jvm") apply false + id("build-kmp") apply false + } + repositories { + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" +} + +// Включает вот такую конструкцию +//implementation(projects.m2l5Gradle.sub1.ssub1) +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +include(":ok-marketplace-states-common") +include(":ok-marketplace-states-biz") diff --git a/ok-marketplace-tests/gradle.properties b/ok-marketplace-tests/gradle.properties index 28db788..2feb974 100644 --- a/ok-marketplace-tests/gradle.properties +++ b/ok-marketplace-tests/gradle.properties @@ -1,3 +1,4 @@ kotlin.code.style=official kotlin.native.ignoreDisabledTargets=true +org.gradle.jvmargs=-Xmx3g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 #kotlin.native.cacheKind.linuxX64=none diff --git a/ok-marketplace-tests/ok-marketplace-e2e-be/build.gradle.kts b/ok-marketplace-tests/ok-marketplace-e2e-be/build.gradle.kts index dd397ea..f7cbeed 100644 --- a/ok-marketplace-tests/ok-marketplace-e2e-be/build.gradle.kts +++ b/ok-marketplace-tests/ok-marketplace-e2e-be/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("jvm") + id("build-jvm") } dependencies { diff --git a/ok-marketplace-tests/settings.gradle.kts b/ok-marketplace-tests/settings.gradle.kts index 9e4e18c..06116d9 100644 --- a/ok-marketplace-tests/settings.gradle.kts +++ b/ok-marketplace-tests/settings.gradle.kts @@ -24,6 +24,4 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" } -//include(":ok-marketplace-api-v1-jackson") -//include(":ok-marketplace-api-v2-kmp") include(":ok-marketplace-e2e-be") diff --git a/settings.gradle.kts b/settings.gradle.kts index 30c6c2c..b3e680a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,7 +12,8 @@ rootProject.name = "ok-marketplace-202405" //includeBuild("lessons") includeBuild("ok-marketplace-be") +includeBuild("ok-marketplace-states") includeBuild("ok-marketplace-libs") includeBuild("ok-marketplace-tests") -includeBuild("pgkn") \ No newline at end of file +includeBuild("pgkn")