Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: API middleware redesign #364

Merged
merged 9 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/lib/kotlin/kiit/build.gradle
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -62,7 +63,9 @@ 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'

Expand Down
26 changes: 24 additions & 2 deletions src/lib/kotlin/kiit/src/main/kotlin/kiit/Server.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Server(val ctx: Context) {
suspend fun execute(): Try<Any> {

// 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()
Expand Down Expand Up @@ -94,7 +94,19 @@ class Server(val ctx: Context) {


fun apis(): List<GlobalVersion> {
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
}

Expand All @@ -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 {

Expand Down
6 changes: 4 additions & 2 deletions src/lib/kotlin/slatekit-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions src/lib/kotlin/slatekit-tests/settings.gradle
Original file line number Diff line number Diff line change
@@ -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"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}


Expand Down Expand Up @@ -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)
Expand All @@ -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])
}


Expand Down Expand Up @@ -353,6 +362,7 @@ class Api_001_Loader_Tests : ApiTestsBase() {
"access" : "public",
"sources" : ["all"],
"version" : "0",
"policies" : [],
"tags" : [],
"actions" : [
{
Expand All @@ -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",
Expand All @@ -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" }
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<ApiRequest>()

override suspend fun process(req: ApiRequest, next: suspend (ApiRequest) -> Outcome<ApiResult>): Outcome<ApiResult> {
middlewareHook.add(req)
return next(req)
}
}


class TestActionMiddleware : Middleware{
// Used for demo/testing purposes
var middlewareHook = mutableListOf<ApiRequest>()

override suspend fun process(req: ApiRequest, next: suspend (ApiRequest) -> Outcome<ApiResult>): Outcome<ApiResult> {
middlewareHook.add(req)
return next(req)
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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<AsyncQueue<String>> = listOf()) : ApiQueueSupport {
override fun queues(): List<AsyncQueue<String>> = queues
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<AsyncQueue<String>> = listOf()) : ApiQueueSupport {
class SampleWorkerAPI(val ctx: AppContext) {

var _lastResult = ""

override fun queues(): List<AsyncQueue<String>> = queues


@Action(tags = ["queued"])
@Action(policies = ["queued"])
fun test1(s: String, b: Boolean, i: Int): String {
_lastResult = "$s, $b, $i"
return _lastResult
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ApiRequest>()


@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()
Expand All @@ -26,14 +34,4 @@ open class SampleMiddlewareApi : Middleware {
fun errored(): Outcome<String> {
return Outcomes.errored("test failed")
}


@Action()
fun hello(): String = "hello world"


override suspend fun process(req: ApiRequest, next: suspend (ApiRequest) -> Outcome<ApiResult>): Outcome<ApiResult> {
middlewareHook.add(req)
return next(req)
}
}
6 changes: 4 additions & 2 deletions src/services/apis/src/main/kotlin/kiit/apis/Annotations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ annotation class Api(
val access: String = AccessLevel.PUBLIC,
val sources: Array<String> = [Sources.ALL],
val version: String = ApiConstants.zero,
val tags: Array<String> = []
val tags: Array<String> = [],
val policies: Array<String> = []
)

/**
Expand All @@ -67,7 +68,8 @@ annotation class Action(
val access: String = AccessLevel.PARENT,
val sources: Array<String> = [Sources.ALL],
val version: String = ApiConstants.zero,
val tags: Array<String> = []
val tags: Array<String> = [],
val policies: Array<String> = []
)

/**
Expand Down
Loading