From a914bd3228d4428ff6b40b41161b90bfc0f63df1 Mon Sep 17 00:00:00 2001 From: kishore Date: Mon, 18 Dec 2023 13:42:58 -0500 Subject: [PATCH 1/9] fix: middleware support --- .../kotlin/test/apis/Api_001_Loader_Tests.kt | 28 ++++++++---- .../kotlin/test/apis/Api_MIddleware_Tests.kt | 44 ++++++++++++++++--- .../test/kotlin/test/setup/SampleAnnoApi.kt | 8 +++- .../kotlin/test/setup/SampleMiddlewareApi.kt | 26 +++++------ .../src/main/kotlin/kiit/apis/Annotations.kt | 6 ++- .../src/main/kotlin/kiit/apis/ApiServer.kt | 18 ++++---- .../main/kotlin/kiit/apis/setup/AnnoLoader.kt | 9 ++-- .../kotlin/kiit/apis/setup/ConfigLoader.kt | 6 ++- 8 files changed, 101 insertions(+), 44 deletions(-) diff --git a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/apis/Api_001_Loader_Tests.kt b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/apis/Api_001_Loader_Tests.kt index b4da7182e..7d7c3f352 100644 --- a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/apis/Api_001_Loader_Tests.kt +++ b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/apis/Api_001_Loader_Tests.kt @@ -82,13 +82,13 @@ class Api_001_Loader_Tests : ApiTestsBase() { Assert.assertEquals(true, api.sources.hasAPI()) Assert.assertEquals("add", actions.items[0].path.action.name) - Assert.assertEquals(true, actions.items[0].path.action.roles.isEmpty) + Assert.assertEquals(true , actions.items[0].path.action.roles.isEmpty) Assert.assertEquals(AuthMode.Keyed, actions.items[0].path.action.auth) - Assert.assertEquals(Verb.Post, actions.items[0].path.action.verb) - Assert.assertEquals("0", actions.items[0].path.action.version) - Assert.assertEquals(Access.Public, actions.items[0].path.action.access) - Assert.assertEquals(true, actions.items[0].path.action.sources.hasAPI()) - Assert.assertEquals(2, (actions.items[0].handler as MethodExecutor).call.params.size) + Assert.assertEquals(Verb.Post , actions.items[0].path.action.verb) + Assert.assertEquals("0" , actions.items[0].path.action.version) + Assert.assertEquals(Access.Public , actions.items[0].path.action.access) + Assert.assertEquals(true , actions.items[0].path.action.sources.hasAPI()) + Assert.assertEquals(2 , (actions.items[0].handler as MethodExecutor).call.params.size) } @@ -146,7 +146,12 @@ class Api_001_Loader_Tests : ApiTestsBase() { Assert.assertEquals(Access.Internal, api.access) Assert.assertEquals(false, api.sources.hasAPI()) Assert.assertEquals(true, api.sources.hasCLI()) + Assert.assertEquals("apiTag1", api.tags[0]) + Assert.assertEquals("apiTag2", api.tags[1]) + Assert.assertEquals("apiPolicy1", api.policies[0]) + Assert.assertEquals("apiPolicy2", api.policies[1]) + val action = actions.items[0].path.action Assert.assertEquals("adder", actions.items[0].path.action.name) Assert.assertEquals(true, actions.items[0].path.action.roles.contains("user")) Assert.assertEquals(AuthMode.Keyed, actions.items[0].path.action.auth) @@ -156,6 +161,10 @@ class Api_001_Loader_Tests : ApiTestsBase() { Assert.assertEquals(true, actions.items[0].path.action.sources.hasAPI()) Assert.assertEquals(false, actions.items[0].path.action.sources.hasCLI()) Assert.assertEquals(2, (actions.items[0].handler as MethodExecutor).call.params.size) + Assert.assertEquals("actionTag1", action.tags[0]) + Assert.assertEquals("actionTag2", action.tags[1]) + Assert.assertEquals("actionPolicy1", action.policies[0]) + Assert.assertEquals("actionPolicy2", action.policies[1]) } @@ -353,6 +362,7 @@ class Api_001_Loader_Tests : ApiTestsBase() { "access" : "public", "sources" : ["all"], "version" : "0", + "policies" : [], "tags" : [], "actions" : [ { @@ -373,7 +383,8 @@ class Api_001_Loader_Tests : ApiTestsBase() { "access" : "internal", "sources" : ["cli"], "version" : "1", - "tags" : [], + "policies" : ["apiPolicy1", "apiPolicy2"], + "tags" : ["apiTag1", "apiTag2"], "actions" : [ { "name" : "adder", @@ -384,7 +395,8 @@ class Api_001_Loader_Tests : ApiTestsBase() { "access" : "public", "sources" : ["api"], "version" : "1", - "tags" : [], + "policies" : ["actionPolicy1", "actionPolicy2"], + "tags" : ["actionTag1", "actionTag2"], "execute" : { "type": "method", "target": "add" } } ] diff --git a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/apis/Api_MIddleware_Tests.kt b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/apis/Api_MIddleware_Tests.kt index d9fb5ff99..a2bb57602 100644 --- a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/apis/Api_MIddleware_Tests.kt +++ b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/apis/Api_MIddleware_Tests.kt @@ -21,6 +21,7 @@ import kiit.apis.setup.GlobalVersion import kiit.apis.setup.api import kiit.apis.setup.routes import kiit.results.Codes +import kiit.results.Outcome import kiit.results.getOrElse import test.apis.samples.Sample_API_1_Core import test.setup.SampleMiddlewareApi @@ -34,15 +35,48 @@ class Api_Middleware_Tests : ApiTestsBase() { @Test fun can_handle_hooks() { + val apiMiddleware = TestApiMiddleware() + val actionMiddleware = TestActionMiddleware() val api = SampleMiddlewareApi() + val policies = listOf( + "api_test" to apiMiddleware, + "action_test" to actionMiddleware + ) val routes = routes(versions = listOf(GlobalVersion("0", listOf(api(SampleMiddlewareApi::class, api))))) - val apis = ApiServer(ctx, routes = routes) - val r1 = runBlocking { apis.executeAttempt("app", "SampleMiddleware", SampleMiddlewareApi::hello.name, Verb.Post, mapOf(), mapOf()) } + val apis = ApiServer(ctx, routes = routes, middleware = policies ) + val r1 = runBlocking { apis.executeAttempt("app", "SampleMiddleware", SampleMiddlewareApi::hi.name, Verb.Post, mapOf(), mapOf()) } val r2 = runBlocking { apis.executeAttempt("app", "SampleMiddleware", SampleMiddlewareApi::hello.name, Verb.Post, mapOf(), mapOf()) } - Assert.assertTrue(api.middlewareHook.size == 2) - Assert.assertTrue(api.middlewareHook[0].request.path == "app.SampleMiddleware.hello") - Assert.assertTrue(api.middlewareHook[1].request.path == "app.SampleMiddleware.hello") + Assert.assertTrue(apiMiddleware.middlewareHook.size == 2) + Assert.assertTrue(apiMiddleware.middlewareHook[0].request.path == "app.SampleMiddleware.hi") + Assert.assertTrue(apiMiddleware.middlewareHook[1].request.path == "app.SampleMiddleware.hello") + + Assert.assertTrue(actionMiddleware.middlewareHook.size == 2) + Assert.assertTrue(actionMiddleware.middlewareHook[0].request.path == "app.SampleMiddleware.hi") + Assert.assertTrue(actionMiddleware.middlewareHook[1].request.path == "app.SampleMiddleware.hello") + Assert.assertEquals("hi world", r1.getOrNull()) Assert.assertEquals("hello world", r2.getOrNull()) } } + + +class TestApiMiddleware : Middleware{ + // Used for demo/testing purposes + var middlewareHook = mutableListOf() + + override suspend fun process(req: ApiRequest, next: suspend (ApiRequest) -> Outcome): Outcome { + middlewareHook.add(req) + return next(req) + } +} + + +class TestActionMiddleware : Middleware{ + // Used for demo/testing purposes + var middlewareHook = mutableListOf() + + override suspend fun process(req: ApiRequest, next: suspend (ApiRequest) -> Outcome): Outcome { + middlewareHook.add(req) + return next(req) + } +} \ No newline at end of file diff --git a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/setup/SampleAnnoApi.kt b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/setup/SampleAnnoApi.kt index 0b45faf25..46512f812 100644 --- a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/setup/SampleAnnoApi.kt +++ b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/setup/SampleAnnoApi.kt @@ -31,9 +31,13 @@ class SampleApiWithConfigSetup() { } @Api(version = "1", area = "tests", name = "overrides", desc = "sample to test features of Kiit APIs", - auth = AuthModes.TOKEN, roles = ["admin"], verb = Verbs.AUTO, access = AccessLevel.INTERNAL, sources = [Sources.CLI]) + auth = AuthModes.TOKEN, roles = ["admin"], verb = Verbs.AUTO, access = AccessLevel.INTERNAL, sources = [Sources.CLI], + tags = ["apiTag1", "apiTag2"], policies = ["apiPolicy1", "apiPolicy2"]) class SampleAnnotatedApiWithOverrides() { - @Action(version = "1", name = "adder", desc = "accepts supplied basic data types from send", verb = Verbs.PUT, access = AccessLevel.PUBLIC, auth = AuthModes.KEYED, roles = ["user"], sources = [Sources.API]) + + @Action(version = "1", name = "adder", desc = "accepts supplied basic data types from send", verb = Verbs.PUT, + access = AccessLevel.PUBLIC, auth = AuthModes.KEYED, roles = ["user"], sources = [Sources.API], + tags = ["actionTag1", "actionTag2"], policies = ["actionPolicy1", "actionPolicy2"]) fun add(a:Int, b:Int): Int { return a + b } diff --git a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/setup/SampleMiddlewareApi.kt b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/setup/SampleMiddlewareApi.kt index 8b0d1a9e8..9ffcc9f14 100644 --- a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/setup/SampleMiddlewareApi.kt +++ b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/setup/SampleMiddlewareApi.kt @@ -5,15 +5,23 @@ import kiit.results.* import kiit.results.builders.Outcomes -@Api(area = "app", name = "SampleMiddleware", desc = "api to access and manage users 3", auth = AuthModes.NONE) -open class SampleMiddlewareApi : Middleware { +@Api(area = "app", name = "SampleMiddleware", desc = "api to access and manage users 3", auth = AuthModes.NONE, policies = ["api_test"]) +open class SampleMiddlewareApi { // Used for demo/testing purposes var middlewareHook = mutableListOf() - @Action() - fun hi(): String = "hi world" + @Action(policies = ["action_test"]) + fun hi(): String { + return "hi world" + } + + + @Action(policies = ["action_test"]) + fun hello(): String { + return "hello world" + } @Action() @@ -26,14 +34,4 @@ open class SampleMiddlewareApi : Middleware { fun errored(): Outcome { return Outcomes.errored("test failed") } - - - @Action() - fun hello(): String = "hello world" - - - override suspend fun process(req: ApiRequest, next: suspend (ApiRequest) -> Outcome): Outcome { - middlewareHook.add(req) - return next(req) - } } \ No newline at end of file diff --git a/src/services/apis/src/main/kotlin/kiit/apis/Annotations.kt b/src/services/apis/src/main/kotlin/kiit/apis/Annotations.kt index 544d1c2b9..2baaf8d02 100644 --- a/src/services/apis/src/main/kotlin/kiit/apis/Annotations.kt +++ b/src/services/apis/src/main/kotlin/kiit/apis/Annotations.kt @@ -40,7 +40,8 @@ annotation class Api( val access: String = AccessLevel.PUBLIC, val sources: Array = [Sources.ALL], val version: String = ApiConstants.zero, - val tags: Array = [] + val tags: Array = [], + val policies: Array = [] ) /** @@ -67,7 +68,8 @@ annotation class Action( val access: String = AccessLevel.PARENT, val sources: Array = [Sources.ALL], val version: String = ApiConstants.zero, - val tags: Array = [] + val tags: Array = [], + val policies: Array = [] ) /** diff --git a/src/services/apis/src/main/kotlin/kiit/apis/ApiServer.kt b/src/services/apis/src/main/kotlin/kiit/apis/ApiServer.kt index 800abd8f8..5c476d195 100644 --- a/src/services/apis/src/main/kotlin/kiit/apis/ApiServer.kt +++ b/src/services/apis/src/main/kotlin/kiit/apis/ApiServer.kt @@ -30,8 +30,8 @@ import kotlin.reflect.KClass /** * This is the core container hosting, managing and executing the source independent apis. - * @param ctx : Context of the environment @see[slatekti.common.Context] - * @param apis : APIs to host/serve + * @param ctx : Context of the environment @see[kiit.common.Context] + * @param routes : APIs to host/serve * @param middleware : Hooks and middleware for filters, conversions, execution * @param settings : Settings for the server */ @@ -39,7 +39,7 @@ open class ApiServer( val ctx: Context, val routes: List, val rewriter: Rewriter? = null, - val namedMiddlewares: List> = listOf(), + middleware: List> = listOf(), val auth: Auth? = null, deserializer: Deserializer? = null, metas : List, MetaHandler>> = listOf(), @@ -50,17 +50,20 @@ open class ApiServer( * Load all the routes from the APIs supplied. * The API setup can be either annotation based or public methods on the Class */ - val router = Router(routes, settings.naming) + private val router = Router(routes, settings.naming) /** * Decoder for converting Request Body JSON to method parameter values. */ - val decoder: Deserializer = deserializer ?: JsonDeserializer(ctx.enc) + private val decoder: Deserializer = deserializer ?: JsonDeserializer(ctx.enc) + + + private val policies = middleware.toMap() /** * Builds/Deserializes types from the Request and its metadata */ - val executor : Executor = Executor(decoder, MetaDecoder(metas)) + val executor : Executor = Executor(decoder, MetaDecoder(metas), policies) /** * The help class to handle help on an area, api, or action @@ -73,9 +76,6 @@ open class ApiServer( private val logger: Logger = ctx.logs.getLogger("api") - private val middlewares = namedMiddlewares.map { it.second } - - /** * Initialize all the routes with reference to this server */ diff --git a/src/services/apis/src/main/kotlin/kiit/apis/setup/AnnoLoader.kt b/src/services/apis/src/main/kotlin/kiit/apis/setup/AnnoLoader.kt index 80c3765c8..c56aab982 100644 --- a/src/services/apis/src/main/kotlin/kiit/apis/setup/AnnoLoader.kt +++ b/src/services/apis/src/main/kotlin/kiit/apis/setup/AnnoLoader.kt @@ -38,7 +38,8 @@ class AnnoLoader(val cls: KClass<*>, val instance: Any, val namer: Namer?) { Sources(anno.sources.toList().map { Source.parse(it) }), Verb.parse(anno.verb), anno.version, - anno.tags.toList() + anno.policies.toList(), + anno.tags.toList(), ) return api @@ -50,7 +51,8 @@ class AnnoLoader(val cls: KClass<*>, val instance: Any, val namer: Namer?) { val actionNameRaw = apiAction?.name.orElse(methodName) val actionName = namer?.rename(actionNameRaw) ?: actionNameRaw val actionDesc = apiAction.desc ?: "" - val actionTags = apiAction?.tags?.toList() ?: listOf() + val actionTags = apiAction.tags.toList() + val actionPolicies = apiAction.policies.toList() // Default these from api if empty val actionAuth = References.auth(api.auth, apiAction.auth) @@ -69,7 +71,8 @@ class AnnoLoader(val cls: KClass<*>, val instance: Any, val namer: Namer?) { actionProtocol, actionVerb, actionVersion, - actionTags.toList() + actionPolicies.toList(), + actionTags.toList(), ) } } diff --git a/src/services/apis/src/main/kotlin/kiit/apis/setup/ConfigLoader.kt b/src/services/apis/src/main/kotlin/kiit/apis/setup/ConfigLoader.kt index 26f2aa522..203c89001 100644 --- a/src/services/apis/src/main/kotlin/kiit/apis/setup/ConfigLoader.kt +++ b/src/services/apis/src/main/kotlin/kiit/apis/setup/ConfigLoader.kt @@ -61,7 +61,8 @@ class ConfigLoader(val cls: KClass<*>, val instance: Any) { val access = doc.get("access") as String val sources = toList(doc.get("sources") as JSONArray).map { Source.parse(it) } val version = doc.get("version") as String - val tags = toList(doc.get("tags") as JSONArray) + val policies = toList(doc.get("policies") as JSONArray?) + val tags = toList(doc.get("tags") as JSONArray?) val api = Api( area, name, @@ -72,6 +73,7 @@ class ConfigLoader(val cls: KClass<*>, val instance: Any) { Sources(sources), Verb.parse(verb), version, + policies, tags ) return api @@ -118,6 +120,7 @@ class ConfigLoader(val cls: KClass<*>, val instance: Any) { val access = doc.get("access") as String? val sources = toList(doc.get("sources") as JSONArray?) val version = doc.get("version") as String? + val policies = toList(doc.get("policies") as JSONArray?) val tags = toList(doc.get("tags") as JSONArray?) // Override with api ( if null or reference to parent via "@parent" @@ -145,6 +148,7 @@ class ConfigLoader(val cls: KClass<*>, val instance: Any) { actionSources, actionVerb, actionVersion, + policies, tags ) return action From 14296a2bb3f3c2e0148a2bd523182d920f2fb70d Mon Sep 17 00:00:00 2001 From: kishore Date: Mon, 18 Dec 2023 13:52:07 -0500 Subject: [PATCH 2/9] fix: queuing middleware --- .../test/kotlin/test/jobs/Worker_Api_Tests.kt | 22 ++++++++++++++----- .../test/jobs/samples/SampleWorkerAPI.kt | 7 ++---- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/jobs/Worker_Api_Tests.kt b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/jobs/Worker_Api_Tests.kt index 9c5346780..a20e5cee5 100644 --- a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/jobs/Worker_Api_Tests.kt +++ b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/jobs/Worker_Api_Tests.kt @@ -1,16 +1,14 @@ package test.jobs +import kiit.apis.* +import kiit.apis.Middleware import kotlinx.coroutines.runBlocking import org.junit.Assert import org.junit.Test import org.threeten.bp.ZoneId -import kiit.apis.ApiServer -import kiit.apis.Verb import kiit.apis.routes.Api import kiit.apis.core.Reqs -import kiit.apis.SetupType -import kiit.apis.Verbs import kiit.apis.setup.GlobalVersion import kiit.apis.setup.api import kiit.apis.setup.routes @@ -24,7 +22,9 @@ import kiit.core.queues.AsyncQueue import kiit.core.queues.WrappedAsyncQueue import kiit.connectors.jobs.JobAPIWorker import kiit.connectors.jobs.JobQueue +import kiit.integration.common.ApiQueueSupport import kiit.jobs.* +import kiit.results.Outcome import test.apis.samples.Sample_API_1_Core import test.jobs.samples.SampleWorkerAPI import test.setup.SampleTypes2Api @@ -51,11 +51,15 @@ class Worker_Api_Tests : TestSupport { val queues = listOf(AsyncQueue.of(InMemoryQueue.stringQueue())) // 3. apis - val api = SampleWorkerAPI(ctx, queues) + val api = SampleWorkerAPI(ctx) + + // 4. Policy( Middleware ) + val policy = TestQueueingMiddleware(queues) + val policies = listOf("queued" to policy) // 4. container val routes = routes(versions = listOf(GlobalVersion("0", listOf(api(SampleWorkerAPI::class, api))))) - val apis = ApiServer(ctx, routes = routes) + val apis = ApiServer(ctx, routes = routes, middleware = policies) // 5. send method call to queue val result = runBlocking { @@ -122,3 +126,9 @@ class Worker_Api_Tests : TestSupport { //Assert.assertTrue( result.getOrElse { null } == "user1@abc.com, true, 123, 2018-01-27T09:30:45Z" ) } } + + + +class TestQueueingMiddleware(val queues:List> = listOf()) : ApiQueueSupport { + override fun queues(): List> = queues +} \ No newline at end of file diff --git a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/jobs/samples/SampleWorkerAPI.kt b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/jobs/samples/SampleWorkerAPI.kt index 53e9326e9..16b7e86d2 100644 --- a/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/jobs/samples/SampleWorkerAPI.kt +++ b/src/lib/kotlin/slatekit-tests/src/test/kotlin/test/jobs/samples/SampleWorkerAPI.kt @@ -7,14 +7,11 @@ import kiit.integration.common.ApiQueueSupport @Api(area = "samples", name = "workerqueue", desc = "sample api to integrating workers, queues, apis") -class SampleWorkerAPI(val ctx: AppContext, val queues:List> = listOf()) : ApiQueueSupport { +class SampleWorkerAPI(val ctx: AppContext) { var _lastResult = "" - override fun queues(): List> = queues - - - @Action(tags = ["queued"]) + @Action(policies = ["queued"]) fun test1(s: String, b: Boolean, i: Int): String { _lastResult = "$s, $b, $i" return _lastResult From f30f6330806ab6cf384af0de85e51061c0ddc0ef Mon Sep 17 00:00:00 2001 From: kishore Date: Mon, 18 Dec 2023 13:53:51 -0500 Subject: [PATCH 3/9] Update build.gradle --- src/lib/kotlin/slatekit-tests/build.gradle | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/kotlin/slatekit-tests/build.gradle b/src/lib/kotlin/slatekit-tests/build.gradle index 0678bbe94..640d3cb37 100644 --- a/src/lib/kotlin/slatekit-tests/build.gradle +++ b/src/lib/kotlin/slatekit-tests/build.gradle @@ -17,6 +17,7 @@ plugins { id "java" id "maven-publish" id "org.jetbrains.kotlin.jvm" // version "$kotlin_version" + } //apply plugin: 'org.junit.platform.gradle.plugin' @@ -49,15 +50,16 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.0' + //implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.1" implementation "com.googlecode.json-simple:json-simple:1.1" implementation 'com.squareup.okhttp3:okhttp:3.9.0' - implementation "mysql:mysql-connector-java:5.1.13" + implementation "mysql:mysql-connector-java:5.1.48" // https://mvnrepository.com/artifact/com.h2database/h2 implementation group: 'com.h2database', name: 'h2' , version: '1.4.200' implementation group: 'org.xerial' , name:'sqlite-jdbc', version:'3.8.11.2' //implementation "com.h2database:h2:1.4.200" implementation 'org.koin:koin-core:2.0.0-rc-2' - implementation "org.json:json:20201115" + implementation "org.json:json:20230227" //implementation group: 'org.json', name: 'json', version: '20201115' //implementation "postgresql:postgresql:42.1.1" From c4d3a7ac10cbd5cc6bbddb158cddf5cb343cdd3f Mon Sep 17 00:00:00 2001 From: kishore Date: Mon, 18 Dec 2023 13:54:05 -0500 Subject: [PATCH 4/9] Update settings.gradle --- src/lib/kotlin/slatekit-tests/settings.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/kotlin/slatekit-tests/settings.gradle b/src/lib/kotlin/slatekit-tests/settings.gradle index 13e61c90c..206961afc 100644 --- a/src/lib/kotlin/slatekit-tests/settings.gradle +++ b/src/lib/kotlin/slatekit-tests/settings.gradle @@ -1,6 +1,8 @@ pluginManagement { plugins { id "org.jetbrains.kotlin.jvm" version "1.6.20" +// id 'org.jetbrains.kotlin.multiplatform' version "1.6.1" +// id 'org.jetbrains.kotlin.plugin.serialization' version "1.6.1" } } From 24cc7766a0742ddc0304042e218ebfd4abdc34c1 Mon Sep 17 00:00:00 2001 From: kishore Date: Mon, 18 Dec 2023 13:54:37 -0500 Subject: [PATCH 5/9] Update build.gradle --- src/lib/kotlin/kiit/build.gradle | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/kotlin/kiit/build.gradle b/src/lib/kotlin/kiit/build.gradle index 05518f85d..8d291809a 100644 --- a/src/lib/kotlin/kiit/build.gradle +++ b/src/lib/kotlin/kiit/build.gradle @@ -1,5 +1,6 @@ buildscript { ext.kotlin_version = '1.6.20' + ext.ktor_version = '1.6.8' ext.kiit_version = file('../../../version.txt').text ext.kiit_version_beta = file('../../../version-beta.txt').text @@ -62,11 +63,13 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.0' -// implementation "mysql:mysql-connector-java:5.1.13" + implementation "io.ktor:ktor-server-netty:$ktor_version" + implementation "io.ktor:ktor-metrics:$ktor_version" +// implementation "mysql:mysql-connector-java:5.1.48" // implementation 'com.squareup.okhttp3:okhttp:3.9.0' implementation 'org.threeten:threetenbp:1.3.8' - // /* + /* if( kiitSetupViaBinary ) { implementation "dev.kiit:results:$kiit_version" implementation "dev.kiit:common:$kiit_version" @@ -131,7 +134,7 @@ dependencies { // implementation project(":kiit-integration") implementation project(":kiit-server") // implementation project(":slatekit-samples") - } // + // } // // implementation project(":slatekit-examples") // implementation project(":slatekit-samples") implementation project(":kiit-tests") From f7c37018f42ec0de2287ca5cd83658b24e18f3ca Mon Sep 17 00:00:00 2001 From: kishore Date: Mon, 18 Dec 2023 13:56:04 -0500 Subject: [PATCH 6/9] build: update setup for source vs binary --- src/lib/kotlin/kiit/build.gradle | 4 ++-- src/support/generator/build.gradle | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/kotlin/kiit/build.gradle b/src/lib/kotlin/kiit/build.gradle index 8d291809a..aba13d2fd 100644 --- a/src/lib/kotlin/kiit/build.gradle +++ b/src/lib/kotlin/kiit/build.gradle @@ -69,7 +69,7 @@ dependencies { // implementation 'com.squareup.okhttp3:okhttp:3.9.0' implementation 'org.threeten:threetenbp:1.3.8' - /* + // /* if( kiitSetupViaBinary ) { implementation "dev.kiit:results:$kiit_version" implementation "dev.kiit:common:$kiit_version" @@ -134,7 +134,7 @@ dependencies { // implementation project(":kiit-integration") implementation project(":kiit-server") // implementation project(":slatekit-samples") - // } // + } // // implementation project(":slatekit-examples") // implementation project(":slatekit-samples") implementation project(":kiit-tests") diff --git a/src/support/generator/build.gradle b/src/support/generator/build.gradle index 06811dbd6..c1b93f4bf 100644 --- a/src/support/generator/build.gradle +++ b/src/support/generator/build.gradle @@ -55,6 +55,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.0' + implementation "com.googlecode.json-simple:json-simple:1.1" implementation 'com.squareup.okhttp3:okhttp:3.9.0' implementation 'org.threeten:threetenbp:1.3.8' From 521994a08f337a51bc6e083dd55a9fb3a42f0576 Mon Sep 17 00:00:00 2001 From: kishore Date: Mon, 18 Dec 2023 14:56:29 -0500 Subject: [PATCH 7/9] fix: api request handling with versions in paths --- .../src/main/kotlin/kiit/apis/ApiServer.kt | 7 +- .../main/kotlin/kiit/server/ServerContext.kt | 3 +- .../kotlin/kiit/server/ktor/KtorHandler.kt | 71 ++++++++++++------- .../kotlin/kiit/server/ktor/KtorRequest.kt | 11 ++- 4 files changed, 60 insertions(+), 32 deletions(-) diff --git a/src/services/apis/src/main/kotlin/kiit/apis/ApiServer.kt b/src/services/apis/src/main/kotlin/kiit/apis/ApiServer.kt index 5c476d195..d9a4fcdac 100644 --- a/src/services/apis/src/main/kotlin/kiit/apis/ApiServer.kt +++ b/src/services/apis/src/main/kotlin/kiit/apis/ApiServer.kt @@ -109,7 +109,7 @@ open class ApiServer( */ fun get(req: Request): Route? { val gblVersion = req.version - val apiVersion = req.meta.getStringOrNull("x-api-version") + val apiVersion = req.meta.getStringOrNull(VERSION_HEADER_KEY) return get(req.verb, req.area, req.name, req.action, gblVersion, apiVersion) } @@ -198,7 +198,7 @@ open class ApiServer( if(!routeResult) return Outcomes.invalid("Route ${request.request.path} invalid") // Target - val targetResult = get(request.request.verb, request.request.area, request.request.name, request.request.action) + val targetResult = get(request.request) if(targetResult == null) return Outcomes.invalid("Unable to find action") val req = request.copy(target = targetResult) @@ -237,6 +237,9 @@ open class ApiServer( companion object { + + const val VERSION_HEADER_KEY = "x-api-version" + @JvmStatic fun of(ctx: Context, routes: List, auth: Auth? = null, source: Source? = null): ApiServer { val server = ApiServer(ctx, routes, null, listOf(), auth, null, listOf(), Settings(source ?: Source.API)) diff --git a/src/services/server/src/main/kotlin/kiit/server/ServerContext.kt b/src/services/server/src/main/kotlin/kiit/server/ServerContext.kt index 37d76fc53..a1721e716 100644 --- a/src/services/server/src/main/kotlin/kiit/server/ServerContext.kt +++ b/src/services/server/src/main/kotlin/kiit/server/ServerContext.kt @@ -7,10 +7,11 @@ import kiit.common.log.Logger import kiit.telemetry.MetricsLite import kiit.requests.Request import kiit.serialization.deserializer.Deserializer +import org.json.simple.JSONObject data class ServerContext( val logs: Logger, val auth: Authenticator, val metrics: MetricsLite, val stats: Diagnostics, - val decoder: (Request, Encryptor?) -> Deserializer) \ No newline at end of file + val decoder: (Request, Encryptor?) -> Deserializer) \ No newline at end of file diff --git a/src/services/server/src/main/kotlin/kiit/server/ktor/KtorHandler.kt b/src/services/server/src/main/kotlin/kiit/server/ktor/KtorHandler.kt index 67e51f26e..6989a8479 100644 --- a/src/services/server/src/main/kotlin/kiit/server/ktor/KtorHandler.kt +++ b/src/services/server/src/main/kotlin/kiit/server/ktor/KtorHandler.kt @@ -27,34 +27,51 @@ class KtorHandler( override val responses: ResponseHandler = KtorResponse(settings) ) : RequestHandler { - override fun register(routes:Routing){ - - routes.get(settings.prefix + "/help") { - exec(call) - } - routes.get(settings.prefix + "/*/help") { - exec(call) - } - routes.get(settings.prefix + "/*/*/help") { - exec(call) - } - routes.get(settings.prefix + "/*/*/*/help") { - exec(call) - } - routes.get(settings.prefix + "/*/*/*") { - exec(call) + private fun path(prefix: String, path:String, version:String?) : String { + val finalPrefix = if(prefix.endsWith("/")) prefix else "${prefix}/" + val fullPath = when (version) { + null -> "${finalPrefix}${path}" + else -> "${finalPrefix}${version}/${path}" } - routes.post(settings.prefix + "/*/*/*") { - exec(call) - } - routes.put(settings.prefix + "/*/*/*") { - exec(call) - } - routes.patch(settings.prefix + "/*/*/*") { - exec(call) - } - routes.delete(settings.prefix + "/*/*/*") { - exec(call) + println("full path = $fullPath") + return fullPath + } + + + override fun register(routes:Routing){ + val explicitVersions = container.routes.map { it.version }.distinct() + // This is for backwards compatibility /{area}/{api}/{action} ( without version ) + // This defaults version to "version" = 0 + val implicitVersion = listOf(null) + val versions = implicitVersion + explicitVersions + versions.forEach { version -> + routes.get(path(settings.prefix, "help", version)) { + exec(call) + } + routes.get(path(settings.prefix, "*/help", version)) { + exec(call) + } + routes.get(path(settings.prefix, "*/*/help", version)) { + exec(call) + } + routes.get(path(settings.prefix, "*/*/*/help", version)) { + exec(call) + } + routes.get(path(settings.prefix, "*/*/*", version)) { + exec(call) + } + routes.post(path(settings.prefix, "*/*/*", version)) { + exec(call) + } + routes.put(path(settings.prefix, "*/*/*", version)) { + exec(call) + } + routes.patch(path(settings.prefix, "*/*/*", version)) { + exec(call) + } + routes.delete(path(settings.prefix, "*/*/*", version)) { + exec(call) + } } } diff --git a/src/services/server/src/main/kotlin/kiit/server/ktor/KtorRequest.kt b/src/services/server/src/main/kotlin/kiit/server/ktor/KtorRequest.kt index e9998ebb3..0d78b735c 100644 --- a/src/services/server/src/main/kotlin/kiit/server/ktor/KtorRequest.kt +++ b/src/services/server/src/main/kotlin/kiit/server/ktor/KtorRequest.kt @@ -21,6 +21,7 @@ import kiit.common.types.ContentFile import kiit.requests.Request import kiit.requests.RequestSupport import kiit.common.Source +import kiit.common.ext.tail import kiit.common.utils.Random import kiit.common.values.Inputs import kiit.common.values.Metadata @@ -50,7 +51,7 @@ data class KtorRequest( override val raw: Any? = null, override val output: String? = "", override val tag: String = "", - override val version: String = "1.0", + override val version: String = "0", override val timestamp: DateTime = DateTime.now() ) : Request, RequestSupport { @@ -131,7 +132,12 @@ data class KtorRequest( } else { rawUri } - val parts = uri.split('/') + // 1. no version: /{area}/{api}/{action} + // 2. w/ version: /1/{area}/{api}/${action} + val rawParts = uri.split('/') + val has3PartPath = rawParts.size == 3 + val parts = if(has3PartPath) rawParts else rawParts.tail() + val version = if(has3PartPath) "0" else rawParts[0] // val headers = req.headers().map { key -> Pair(key, req.headers(key)) }.toMap() val method = req.httpMethod.value.toLowerCase() @@ -155,6 +161,7 @@ data class KtorRequest( meta = KtorHeaders(req, ctx.enc), data = KtorParams(body, req, ctx.enc), raw = call.request, + version = version, tag = Random.uuid() ) } From e085b7de611a96162736596c710637cb33537f34 Mon Sep 17 00:00:00 2001 From: kishore Date: Mon, 18 Dec 2023 15:00:00 -0500 Subject: [PATCH 8/9] fix: defaulting support for version 0 --- .../server/src/main/kotlin/kiit/server/ServerSettings.kt | 1 + .../server/src/main/kotlin/kiit/server/ktor/KtorHandler.kt | 2 +- .../server/src/main/kotlin/kiit/server/ktor/KtorRequest.kt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/services/server/src/main/kotlin/kiit/server/ServerSettings.kt b/src/services/server/src/main/kotlin/kiit/server/ServerSettings.kt index 10d98eb5f..56f1e0ed0 100644 --- a/src/services/server/src/main/kotlin/kiit/server/ServerSettings.kt +++ b/src/services/server/src/main/kotlin/kiit/server/ServerSettings.kt @@ -23,6 +23,7 @@ package kiit.server data class ServerSettings( val port: Int = 5000, val prefix: String = "/api/", + val versionDefault:String = "0", val info: Boolean = true, val docs: Boolean = false, val docKey: String = "", diff --git a/src/services/server/src/main/kotlin/kiit/server/ktor/KtorHandler.kt b/src/services/server/src/main/kotlin/kiit/server/ktor/KtorHandler.kt index 6989a8479..4efb7e5a5 100644 --- a/src/services/server/src/main/kotlin/kiit/server/ktor/KtorHandler.kt +++ b/src/services/server/src/main/kotlin/kiit/server/ktor/KtorHandler.kt @@ -24,7 +24,7 @@ class KtorHandler( val settings: ServerSettings, override val container:ApiServer, override val diagnostics: Diagnostics = diagnostics(context), - override val responses: ResponseHandler = KtorResponse(settings) + override val responses: ResponseHandler = KtorResponse(settings), ) : RequestHandler { private fun path(prefix: String, path:String, version:String?) : String { diff --git a/src/services/server/src/main/kotlin/kiit/server/ktor/KtorRequest.kt b/src/services/server/src/main/kotlin/kiit/server/ktor/KtorRequest.kt index 0d78b735c..e3ad6ffe9 100644 --- a/src/services/server/src/main/kotlin/kiit/server/ktor/KtorRequest.kt +++ b/src/services/server/src/main/kotlin/kiit/server/ktor/KtorRequest.kt @@ -137,7 +137,7 @@ data class KtorRequest( val rawParts = uri.split('/') val has3PartPath = rawParts.size == 3 val parts = if(has3PartPath) rawParts else rawParts.tail() - val version = if(has3PartPath) "0" else rawParts[0] + val version = if(has3PartPath) settings.versionDefault else rawParts[0] // val headers = req.headers().map { key -> Pair(key, req.headers(key)) }.toMap() val method = req.httpMethod.value.toLowerCase() From 8278d2f44b04ae45013ccd1b27684f91c890183c Mon Sep 17 00:00:00 2001 From: kishore Date: Mon, 18 Dec 2023 15:00:24 -0500 Subject: [PATCH 9/9] fix: sample server --- .../kiit/src/main/kotlin/kiit/Server.kt | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/lib/kotlin/kiit/src/main/kotlin/kiit/Server.kt b/src/lib/kotlin/kiit/src/main/kotlin/kiit/Server.kt index 307c71b35..d80f37082 100644 --- a/src/lib/kotlin/kiit/src/main/kotlin/kiit/Server.kt +++ b/src/lib/kotlin/kiit/src/main/kotlin/kiit/Server.kt @@ -42,7 +42,7 @@ class Server(val ctx: Context) { suspend fun execute(): Try { // 1. Settings - val settings = ServerSettings(port = 5200, prefix = "/api/", docs = true, docKey = "abc123", formatJson = true) + val settings = ServerSettings(port = 5200, prefix = "/api/", docs = true, docKey = "abc123", formatJson = true, versionDefault = "v0") // 2. APIs ( these are Slate Kit Universal APIs ) val apis = apis() @@ -94,7 +94,19 @@ class Server(val ctx: Context) { fun apis(): List { - val apis = listOf(GlobalVersion("0", listOf(api(SampleFiles3Api::class, SampleFiles3Api())))) + val apis = listOf( + GlobalVersion("v0", + listOf( + api(SampleFiles3Api::class, SampleFiles3Api()), + api(SampleVersionApi::class, SampleVersionApi("0")), + ) + ), + GlobalVersion("v1", + listOf( + api(SampleVersionApi::class, SampleVersionApi("1")), + ) + ) + ) return apis } @@ -111,6 +123,16 @@ class Server(val ctx: Context) { +@Api(area = "samples", name = "versioning", desc = "sample api to test other features") +class SampleVersionApi(val version:String) { + + @Action() + fun getHi(request: Request): String { + return "version=${version}" + } +} + + @kiit.apis.Api(area = "samples", name = "files", desc = "sample api to test other features") class SampleFiles3Api {