diff --git a/frontend/kobweb-core/src/jsMain/kotlin/com/varabyte/kobweb/browser/ApiFetcher.kt b/frontend/kobweb-core/src/jsMain/kotlin/com/varabyte/kobweb/browser/ApiFetcher.kt index afa6cac75..668a21650 100644 --- a/frontend/kobweb-core/src/jsMain/kotlin/com/varabyte/kobweb/browser/ApiFetcher.kt +++ b/frontend/kobweb-core/src/jsMain/kotlin/com/varabyte/kobweb/browser/ApiFetcher.kt @@ -27,7 +27,7 @@ class ApiFetcher(private val window: Window) { */ var logOnError: Boolean by window.http::logOnError - private fun toResource(apiPath: String, autoPrefix: Boolean): String { + fun toResource(apiPath: String, autoPrefix: Boolean): String { return RoutePrefix.prependIf(autoPrefix, "/api/${apiPath.trimStart('/')}") } diff --git a/frontend/kobwebx-browser-serialization/README.md b/frontend/kobwebx-core-serialization/README.md similarity index 100% rename from frontend/kobwebx-browser-serialization/README.md rename to frontend/kobwebx-core-serialization/README.md diff --git a/frontend/kobwebx-browser-serialization/build.gradle.kts b/frontend/kobwebx-core-serialization/build.gradle.kts similarity index 86% rename from frontend/kobwebx-browser-serialization/build.gradle.kts rename to frontend/kobwebx-core-serialization/build.gradle.kts index cda054fa8..a9cc9c6fb 100644 --- a/frontend/kobwebx-browser-serialization/build.gradle.kts +++ b/frontend/kobwebx-core-serialization/build.gradle.kts @@ -17,7 +17,7 @@ kotlin { sourceSets { jsMain.dependencies { api(libs.kotlinx.coroutines) - api(projects.frontend.browserExt) // Should this be implementation? + api(projects.frontend.kobwebCore) implementation(libs.kotlinx.serialization.json) } @@ -29,7 +29,7 @@ kotlin { } kobwebPublication { - artifactId.set("kobwebx-browser-serialization") + artifactId.set("kobwebx-core-serialization") description.set("Generally useful Kotlinx Serialization extensions for the browser (not Node) APIs that could potentially move upstream someday.") filter.set(FILTER_OUT_MULTIPLATFORM_PUBLICATIONS) } diff --git a/frontend/kobwebx-core-serialization/src/jsMain/kotlin/com/varabyte/kobweb/browser/ApiFetcherSerializationExt.kt b/frontend/kobwebx-core-serialization/src/jsMain/kotlin/com/varabyte/kobweb/browser/ApiFetcherSerializationExt.kt new file mode 100644 index 000000000..7e96f1ac9 --- /dev/null +++ b/frontend/kobwebx-core-serialization/src/jsMain/kotlin/com/varabyte/kobweb/browser/ApiFetcherSerializationExt.kt @@ -0,0 +1,261 @@ +package com.varabyte.kobweb.browser + +import com.varabyte.kobweb.browser.http.AbortController +import com.varabyte.kobweb.browser.http.delete +import com.varabyte.kobweb.browser.http.get +import com.varabyte.kobweb.browser.http.head +import com.varabyte.kobweb.browser.http.http +import com.varabyte.kobweb.browser.http.options +import com.varabyte.kobweb.browser.http.patch +import com.varabyte.kobweb.browser.http.post +import com.varabyte.kobweb.browser.http.put +import com.varabyte.kobweb.browser.http.tryDelete +import com.varabyte.kobweb.browser.http.tryGet +import com.varabyte.kobweb.browser.http.tryHead +import com.varabyte.kobweb.browser.http.tryOptions +import com.varabyte.kobweb.browser.http.tryPatch +import com.varabyte.kobweb.browser.http.tryPost +import com.varabyte.kobweb.browser.http.tryPut +import kotlinx.browser.window +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.serializer + + +/** + * Call GET on a target API path with [T] as the expected return type. + * + * @param autoPrefix If true AND if a route prefix is configured for this site, auto-affix it to the front. You + * usually want this to be true, unless you are intentionally linking outside this site's root folder while still + * staying in the same domain. + * + * See also [tryGet], which will return null if the request fails for any reason. + * + * Note: you should NOT prepend your path with "api/", as that will be added automatically. + */ +suspend inline fun ApiFetcher.get( + apiPath: String, + headers: Map? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T = window.http.get(toResource(apiPath, autoPrefix), headers, abortController, serializer) + +/** + * Like [get], but returns null if the request failed for any reason. + * + * Additionally, if [ApiFetcher.logOnError] is set to true, any failure will be logged to the console. By default, this will + * be true for debug builds and false for release builds. + */ +suspend inline fun ApiFetcher.tryGet( + apiPath: String, + headers: Map? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T? = window.http.tryGet(toResource(apiPath, autoPrefix), headers, abortController, serializer) + +/** + * Call POST on a target API path with [T] as the expected return type. + * + * @param autoPrefix If true AND if a route prefix is configured for this site, auto-affix it to the front. You + * usually want this to be true, unless you are intentionally linking outside this site's root folder while still + * staying in the same domain. + * + * See also [tryPost], which will return null if the request fails for any reason. + * + * Note: you should NOT prepend your path with "api/", as that will be added automatically. + */ +suspend inline fun ApiFetcher.post( + apiPath: String, + headers: Map? = null, + body: ByteArray? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T = window.http.post(toResource(apiPath, autoPrefix), headers, body, abortController, serializer) + +/** + * Like [post], but returns null if the request failed for any reason. + * + * Additionally, if [ApiFetcher.logOnError] is set to true, any failure will be logged to the console. By default, this will + * be true for debug builds and false for release builds. + */ +suspend inline fun ApiFetcher.tryPost( + apiPath: String, + headers: Map? = null, + body: ByteArray? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T? = window.http.tryPost(toResource(apiPath, autoPrefix), headers, body, abortController, serializer) + +/** + * Call PUT on a target API path with [T] as the expected return type. + * + * @param autoPrefix If true AND if a route prefix is configured for this site, auto-affix it to the front. You + * usually want this to be true, unless you are intentionally linking outside this site's root folder while still + * staying in the same domain. + * + * See also [tryPut], which will return null if the request fails for any reason. + * + * Note: you should NOT prepend your path with "api/", as that will be added automatically. + */ +suspend inline fun ApiFetcher.put( + apiPath: String, + headers: Map? = null, + body: ByteArray? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T = window.http.put(toResource(apiPath, autoPrefix), headers, body, abortController, serializer) + +/** + * Like [put], but returns null if the request failed for any reason. + * + * Additionally, if [ApiFetcher.logOnError] is set to true, any failure will be logged to the console. By default, this will + * be true for debug builds and false for release builds. + */ +suspend inline fun ApiFetcher.tryPut( + apiPath: String, + headers: Map? = null, + body: ByteArray? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T? = window.http.tryPut(toResource(apiPath, autoPrefix), headers, body, abortController, serializer) + +/** + * Call PATCH on a target API path with [T] as the expected return type. + * + * @param autoPrefix If true AND if a route prefix is configured for this site, auto-affix it to the front. You + * usually want this to be true, unless you are intentionally linking outside this site's root folder while still + * staying in the same domain. + * + * See also [tryPatch], which will return null if the request fails for any reason. + * + * Note: you should NOT prepend your path with "api/", as that will be added automatically. + */ +suspend inline fun ApiFetcher.patch( + apiPath: String, + headers: Map? = null, + body: ByteArray? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T = window.http.patch(toResource(apiPath, autoPrefix), headers, body, abortController, serializer) + +/** + * Like [patch], but returns null if the request failed for any reason. + * + * Additionally, if [ApiFetcher.logOnError] is set to true, any failure will be logged to the console. By default, this will + * be true for debug builds and false for release builds. + */ +suspend inline fun ApiFetcher.tryPatch( + apiPath: String, + headers: Map? = null, + body: ByteArray? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T? = window.http.tryPatch(toResource(apiPath, autoPrefix), headers, body, abortController, serializer) + + +/** + * Call DELETE on a target API path with [T] as the expected return type. + * + * @param autoPrefix If true AND if a route prefix is configured for this site, auto-affix it to the front. You + * usually want this to be true, unless you are intentionally linking outside this site's root folder while still + * staying in the same domain. + * + * See also [tryDelete], which will return null if the request fails for any reason. + * + * Note: you should NOT prepend your path with "api/", as that will be added automatically. + */ +suspend inline fun ApiFetcher.delete( + apiPath: String, + headers: Map? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T = window.http.delete(toResource(apiPath, autoPrefix), headers, abortController, serializer) + +/** + * Like [delete], but returns null if the request failed for any reason. + * + * Additionally, if [ApiFetcher.logOnError] is set to true, any failure will be logged to the console. By default, this will + * be true for debug builds and false for release builds. + */ +suspend inline fun ApiFetcher.tryDelete( + apiPath: String, + headers: Map? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T? = window.http.tryDelete(toResource(apiPath, autoPrefix), headers, abortController, serializer) + + +/** + * Call HEAD on a target API path with [T] as the expected return type. + * + * @param autoPrefix If true AND if a route prefix is configured for this site, auto-affix it to the front. You + * usually want this to be true, unless you are intentionally linking outside this site's root folder while still + * staying in the same domain. + * + * See also [tryHead], which will return null if the request fails for any reason. + * + * Note: you should NOT prepend your path with "api/", as that will be added automatically. + */ +suspend inline fun ApiFetcher.head( + apiPath: String, + headers: Map? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T = window.http.head(toResource(apiPath, autoPrefix), headers, abortController, serializer) + +/** + * Like [head], but returns null if the request failed for any reason. + * + * Additionally, if [ApiFetcher.logOnError] is set to true, any failure will be logged to the console. By default, this will + * be true for debug builds and false for release builds. + */ +suspend inline fun ApiFetcher.tryHead( + apiPath: String, + headers: Map? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T? = window.http.tryHead(toResource(apiPath, autoPrefix), headers, abortController, serializer) + +/** + * Call OPTIONS on a target API path with [T] as the expected return type. + * + * @param autoPrefix If true AND if a route prefix is configured for this site, auto-affix it to the front. You + * usually want this to be true, unless you are intentionally linking outside this site's root folder while still + * staying in the same domain. + * + * See also [tryOptions], which will return null if the request fails for any reason. + * + * Note: you should NOT prepend your path with "api/", as that will be added automatically. + */ +suspend inline fun ApiFetcher.options( + apiPath: String, + headers: Map? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T = window.http.options(toResource(apiPath, autoPrefix), headers, abortController, serializer) + +/** + * Like [options], but returns null if the request failed for any reason. + * + * Additionally, if [ApiFetcher.logOnError] is set to true, any failure will be logged to the console. By default, this will + * be true for debug builds and false for release builds. + */ +suspend inline fun ApiFetcher.tryOptions( + apiPath: String, + headers: Map? = null, + abortController: AbortController? = null, + autoPrefix: Boolean = true, + serializer: DeserializationStrategy = serializer() +): T? = window.http.tryOptions(toResource(apiPath, autoPrefix), headers, abortController, serializer) \ No newline at end of file diff --git a/frontend/kobwebx-browser-serialization/src/jsMain/kotlin/com/varabyte/kobweb/browser/http/HttpFetcherSerializationExt.kt b/frontend/kobwebx-core-serialization/src/jsMain/kotlin/com/varabyte/kobweb/browser/http/HttpFetcherSerializationExt.kt similarity index 100% rename from frontend/kobwebx-browser-serialization/src/jsMain/kotlin/com/varabyte/kobweb/browser/http/HttpFetcherSerializationExt.kt rename to frontend/kobwebx-core-serialization/src/jsMain/kotlin/com/varabyte/kobweb/browser/http/HttpFetcherSerializationExt.kt diff --git a/settings.gradle.kts b/settings.gradle.kts index e31d1f004..3d08a5355 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -27,6 +27,7 @@ include(":frontend:kobwebx-markdown") include(":frontend:compose-html-ext") include(":frontend:browser-ext") include(":frontend:test:compose-test-utils") +include(":frontend:kobwebx-core-serialization") include(":backend:kobweb-api") include(":backend:server") include(":backend:server-plugin")