Skip to content

Commit 6bf5441

Browse files
committed
Use new column registry
1 parent 1860276 commit 6bf5441

File tree

11 files changed

+145
-38
lines changed

11 files changed

+145
-38
lines changed

KSP/src/main/kotlin/io/github/jan/supabase/ksp/SelectableSymbolProcessor.kt

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,71 +7,75 @@ import com.google.devtools.ksp.processing.Resolver
77
import com.google.devtools.ksp.processing.SymbolProcessor
88
import com.google.devtools.ksp.symbol.KSAnnotated
99
import com.google.devtools.ksp.symbol.KSClassDeclaration
10+
import com.google.devtools.ksp.symbol.KSFile
1011
import com.google.devtools.ksp.symbol.KSNode
1112
import com.google.devtools.ksp.symbol.KSValueParameter
1213
import com.google.devtools.ksp.symbol.Modifier
1314
import com.squareup.kotlinpoet.FileSpec
1415
import com.squareup.kotlinpoet.FunSpec
15-
import com.squareup.kotlinpoet.PropertySpec
16-
import com.squareup.kotlinpoet.ksp.toClassName
16+
import io.github.jan.supabase.postgrest.Postgrest
1717
import io.github.jan.supabase.postgrest.annotations.ApplyFunction
1818
import io.github.jan.supabase.postgrest.annotations.Cast
1919
import io.github.jan.supabase.postgrest.annotations.ColumnName
2020
import io.github.jan.supabase.postgrest.annotations.Foreign
2121
import io.github.jan.supabase.postgrest.annotations.JsonPath
2222
import io.github.jan.supabase.postgrest.annotations.Selectable
23-
import io.github.jan.supabase.postgrest.query.Columns
2423

2524
class SelectableSymbolProcessor(
2625
private val codeGenerator: CodeGenerator,
2726
private val logger: KSPLogger,
2827
private val options: Map<String, String>
2928
) : SymbolProcessor {
29+
30+
val packageName = options["packageName"] ?: "io.github.jan.supabase.postgrest"
31+
val fileName = options["fileName"] ?: "PostgrestColumns"
32+
3033
override fun process(resolver: Resolver): List<KSAnnotated> {
3134
val symbols = resolver.getSymbolsWithAnnotation(Selectable::class.java.name).filterIsInstance<KSClassDeclaration>()
3235
if (!symbols.iterator().hasNext()) return emptyList()
36+
val types = hashMapOf<String, String>()
3337
symbols.forEach { symbol ->
3438
val className = symbol.simpleName.asString()
35-
val packageName = symbol.containingFile?.packageName?.asString().orEmpty()
39+
val qualifiedName = symbol.qualifiedName?.asString()
40+
if(qualifiedName == null) {
41+
logger.error("Qualified name of $className is null", symbol)
42+
return@forEach;
43+
}
3644
if (!symbol.modifiers.contains(Modifier.DATA)) {
3745
logger.error("The class $className is not a data class", symbol)
3846
return emptyList()
3947
}
40-
val companionObject = symbol.anyCompanionObject()
41-
if(companionObject == null) {
42-
logger.error("Companion object not found", symbol)
43-
return emptyList()
44-
}
4548
val parameters = symbol.primaryConstructor?.parameters
4649
if(parameters == null) {
4750
logger.error("Primary constructor is null or has no parameter", symbol)
4851
return emptyList()
4952
}
5053
val columns = parameters.map { processParameters(it, resolver) }.joinToString(",")
51-
writeColumnsExtensionProperty(symbol, companionObject, columns, "${className}Columns", packageName)
54+
types[qualifiedName] = columns
5255
}
56+
writePostgrestExtensionFunction(types, symbols.mapNotNull { it.containingFile }.toList())
5357
return emptyList()
5458
}
5559

56-
private fun writeColumnsExtensionProperty(
57-
symbol: KSClassDeclaration,
58-
companionObject: KSClassDeclaration,
59-
columns: String,
60-
className: String,
61-
packageName: String
60+
private fun writePostgrestExtensionFunction(
61+
columns: Map<String, String>,
62+
sources: List<KSFile>
6263
) {
63-
val fileSpec = FileSpec.builder(packageName, className)
64-
.addImport("io.github.jan.supabase.postgrest.query", "Columns")
65-
.addProperty(PropertySpec.builder("columns", Columns::class)
66-
.receiver(companionObject.toClassName())
67-
.getter(FunSpec.getterBuilder().addStatement("return Columns.raw(\"$columns\")").build())
68-
.build()
69-
)
64+
//Maybe add comments and SupabaseInternal annotations
65+
val function = FunSpec.builder("addSelectableTypes")
66+
.addKdoc(COMMENT)
67+
.receiver(Postgrest.Config::class)
68+
columns.forEach { (qualifiedName, columns) ->
69+
function.addStatement("columnRegistry.registerColumns(\"$qualifiedName\", \"$columns\")")
70+
}
71+
val fileSpec = FileSpec.builder(packageName, fileName)
72+
.addFunction(function.build())
73+
.addImport("io.github.jan.supabase.postgrest.annotations", "Selectable")
7074
.build()
7175
codeGenerator.createNewFile(
72-
Dependencies(false, symbol.containingFile!!),
76+
Dependencies(false, *sources.toTypedArray()),
7377
packageName,
74-
className,
78+
fileName,
7579
extensionName = "kt"
7680
).bufferedWriter().use {
7781
fileSpec.writeTo(it)
@@ -212,4 +216,15 @@ class SelectableSymbolProcessor(
212216
}
213217
}
214218

219+
companion object {
220+
221+
val COMMENT = """
222+
|Adds the types annotated with [Selectable] to the ColumnRegistry. Allows to use the automatically generated columns in the PostgrestQueryBuilder.
223+
|
224+
|This file is generated by the SelectableSymbolProcessor.
225+
|Do not modify it manually.
226+
""".trimMargin()
227+
228+
}
229+
215230
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.github.jan.supabase.postgrest
2+
3+
import kotlin.reflect.KClass
4+
5+
class ColumnRegistry(
6+
private val map: MutableMap<String, String> = mutableMapOf()
7+
) {
8+
9+
fun <T : Any> getColumns(kClass: KClass<T>): String = map[kClass.qualifiedName] ?: error("No columns registered for $kClass")
10+
11+
fun registerColumns(qualifiedName: String, columns: String) {
12+
map[qualifiedName] = columns
13+
}
14+
15+
}

Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/Postgrest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ interface Postgrest : MainPlugin<Postgrest.Config>, CustomSerializationPlugin {
101101
data class Config(
102102
var defaultSchema: String = "public",
103103
var propertyConversionMethod: PropertyConversionMethod = PropertyConversionMethod.CAMEL_CASE_TO_SNAKE_CASE,
104+
var columnRegistry: ColumnRegistry = ColumnRegistry()
104105
): MainConfig(), CustomSerializationConfig {
105106

106107
override var serializer: SupabaseSerializer? = null

Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ internal class PostgrestImpl(override val supabaseClient: SupabaseClient, overri
5656
override suspend fun rpc(function: String, request: RpcRequestBuilder.() -> Unit): PostgrestResult = rpcRequest(function, null, request)
5757

5858
private suspend fun rpcRequest(function: String, body: JsonObject? = null, request: RpcRequestBuilder.() -> Unit): PostgrestResult {
59-
val requestBuilder = RpcRequestBuilder(config.defaultSchema, config.propertyConversionMethod).apply(request)
59+
val requestBuilder = RpcRequestBuilder(config.defaultSchema, config.propertyConversionMethod, config.columnRegistry).apply(request)
6060
val urlParams = buildMap {
6161
putAll(requestBuilder.params.mapToFirstValue())
6262
if(requestBuilder.method != RpcMethod.POST && body != null) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package io.github.jan.supabase.postgrest.query
2+
3+
import io.github.jan.supabase.annotations.SupabaseExperimental
4+
import io.github.jan.supabase.auth.PostgrestFilterDSL
5+
import io.github.jan.supabase.exceptions.HttpRequestException
6+
import io.github.jan.supabase.exceptions.RestException
7+
import io.github.jan.supabase.postgrest.annotations.Selectable
8+
import io.github.jan.supabase.postgrest.query.request.SelectRequestBuilder
9+
import io.github.jan.supabase.postgrest.result.PostgrestResult
10+
import io.ktor.client.plugins.HttpRequestTimeoutException
11+
12+
/**
13+
* Executes vertical filtering with select on [PostgrestQueryBuilder.table].
14+
*
15+
* - This method is a shorthand for [select] with the columns automatically determined by the [T] type.
16+
* - [T] must be marked with [Selectable] and the `ksp-compiler` KSP dependency must be added to the project (via the KSP Gradle plugin).
17+
*
18+
* @param request Additional configurations for the request including filters
19+
* @return PostgrestResult which is either an error, an empty JsonArray or the data you requested as an JsonArray
20+
* @throws RestException or one of its subclasses if receiving an error response
21+
* @throws HttpRequestTimeoutException if the request timed out
22+
* @throws HttpRequestException on network related issues
23+
*/
24+
@SupabaseExperimental
25+
suspend inline fun <reified T : Any> PostgrestQueryBuilder.select(
26+
request: @PostgrestFilterDSL SelectRequestBuilder.() -> Unit = {}
27+
): PostgrestResult {
28+
val registry = postgrest.config.columnRegistry
29+
val columns = registry.getColumns(T::class)
30+
return select(Columns.raw(columns), request)
31+
}
32+
33+
/**
34+
* Return `data` after the query has been executed.
35+
*
36+
* - This method is a shorthand for [select] with the columns automatically determined by the [T] type.
37+
* - [T] must be marked with [Selectable] and the `ksp-compiler` KSP dependency must be added to the project (via the KSP Gradle plugin).
38+
*/
39+
@SupabaseExperimental
40+
inline fun <reified T : Any> PostgrestRequestBuilder.select() {
41+
val columns = columnRegistry.getColumns(T::class)
42+
select(Columns.raw(columns))
43+
}

Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestQueryBuilder.kt

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ class PostgrestQueryBuilder(
4343
columns: Columns = Columns.ALL,
4444
request: @PostgrestFilterDSL SelectRequestBuilder.() -> Unit = {}
4545
): PostgrestResult {
46-
val requestBuilder = SelectRequestBuilder(postgrest.config.propertyConversionMethod).apply {
46+
val requestBuilder = SelectRequestBuilder(
47+
postgrest.config.propertyConversionMethod,
48+
postgrest.config.columnRegistry
49+
).apply {
4750
request(); params["select"] = listOf(columns.value)
4851
}
4952
val selectRequest = SelectRequest(
@@ -75,7 +78,10 @@ class PostgrestQueryBuilder(
7578
values: List<T>,
7679
request: UpsertRequestBuilder.() -> Unit = {}
7780
): PostgrestResult {
78-
val requestBuilder = UpsertRequestBuilder(postgrest.config.propertyConversionMethod).apply(request)
81+
val requestBuilder = UpsertRequestBuilder(
82+
postgrest.config.propertyConversionMethod,
83+
postgrest.config.columnRegistry
84+
).apply(request)
7985
val body = postgrest.serializer.encodeToJsonElement(values).jsonArray
8086
val columns = body.map { it.jsonObject.keys }.flatten().distinct()
8187
if(columns.isNotEmpty()) requestBuilder.params["columns"] = listOf(columns.joinToString(","))
@@ -129,7 +135,10 @@ class PostgrestQueryBuilder(
129135
values: List<T>,
130136
request: InsertRequestBuilder.() -> Unit = {}
131137
): PostgrestResult {
132-
val requestBuilder = InsertRequestBuilder(postgrest.config.propertyConversionMethod).apply(request)
138+
val requestBuilder = InsertRequestBuilder(
139+
postgrest.config.propertyConversionMethod,
140+
postgrest.config.columnRegistry
141+
).apply(request)
133142
val body = postgrest.serializer.encodeToJsonElement(values).jsonArray
134143
val columns = body.map { it.jsonObject.keys }.flatten().distinct()
135144
if(columns.isNotEmpty()) requestBuilder.params["columns"] = listOf(columns.joinToString(","))
@@ -174,7 +183,7 @@ class PostgrestQueryBuilder(
174183
crossinline update: PostgrestUpdate.() -> Unit = {},
175184
request: PostgrestRequestBuilder.() -> Unit = {}
176185
): PostgrestResult {
177-
val requestBuilder = PostgrestRequestBuilder(postgrest.config.propertyConversionMethod).apply(request)
186+
val requestBuilder = newRequestBuilder(request)
178187
val updateRequest = UpdateRequest(
179188
body = buildPostgrestUpdate(postgrest.config.propertyConversionMethod, postgrest.serializer, update),
180189
returning = requestBuilder.returning,
@@ -201,7 +210,7 @@ class PostgrestQueryBuilder(
201210
value: T,
202211
request: PostgrestRequestBuilder.() -> Unit = {}
203212
): PostgrestResult {
204-
val requestBuilder = PostgrestRequestBuilder(postgrest.config.propertyConversionMethod).apply(request)
213+
val requestBuilder = newRequestBuilder(request)
205214
val updateRequest = UpdateRequest(
206215
returning = requestBuilder.returning,
207216
count = requestBuilder.count,
@@ -226,7 +235,7 @@ class PostgrestQueryBuilder(
226235
suspend inline fun delete(
227236
request: PostgrestRequestBuilder.() -> Unit = {}
228237
): PostgrestResult {
229-
val requestBuilder = PostgrestRequestBuilder(postgrest.config.propertyConversionMethod).apply(request)
238+
val requestBuilder = newRequestBuilder(request)
230239
val deleteRequest = DeleteRequest(
231240
returning = requestBuilder.returning,
232241
count = requestBuilder.count,
@@ -237,6 +246,12 @@ class PostgrestQueryBuilder(
237246
return RestRequestExecutor.execute(postgrest = postgrest, path = table, request = deleteRequest)
238247
}
239248

249+
@PublishedApi
250+
internal inline fun newRequestBuilder(request: PostgrestRequestBuilder.() -> Unit = {}) = PostgrestRequestBuilder(
251+
postgrest.config.propertyConversionMethod,
252+
postgrest.config.columnRegistry
253+
).apply(request)
254+
240255
companion object {
241256
const val HEADER_PREFER = "Prefer"
242257
}

Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestRequestBuilder.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.github.jan.supabase.postgrest.query
33

44
import io.github.jan.supabase.annotations.SupabaseExperimental
55
import io.github.jan.supabase.auth.PostgrestFilterDSL
6+
import io.github.jan.supabase.postgrest.ColumnRegistry
67
import io.github.jan.supabase.postgrest.PropertyConversionMethod
78
import io.github.jan.supabase.postgrest.query.filter.PostgrestFilterBuilder
89
import io.github.jan.supabase.postgrest.result.PostgrestResult
@@ -14,7 +15,10 @@ import kotlin.js.JsName
1415
* A builder for Postgrest requests.
1516
*/
1617
@PostgrestFilterDSL
17-
open class PostgrestRequestBuilder(@PublishedApi internal val propertyConversionMethod: PropertyConversionMethod) {
18+
open class PostgrestRequestBuilder(
19+
@PublishedApi internal val propertyConversionMethod: PropertyConversionMethod,
20+
@PublishedApi internal val columnRegistry: ColumnRegistry
21+
) {
1822

1923
/**
2024
* The [Count] algorithm to use to count rows in the table or view.
@@ -26,7 +30,7 @@ open class PostgrestRequestBuilder(@PublishedApi internal val propertyConversion
2630
* The [Returning] option to use.
2731
*/
2832
var returning: Returning = Returning.Minimal
29-
private set
33+
internal set
3034
@SupabaseExperimental val params: MutableMap<String, List<String>> = mutableMapOf()
3135
@SupabaseExperimental val headers: HeadersBuilder = HeadersBuilder()
3236

Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/InsertRequestBuilder.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package io.github.jan.supabase.postgrest.query.request
22

3+
import io.github.jan.supabase.postgrest.ColumnRegistry
34
import io.github.jan.supabase.postgrest.PropertyConversionMethod
45
import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder
56
import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder
67

78
/**
89
* Request builder for [PostgrestQueryBuilder.insert]
910
*/
10-
open class InsertRequestBuilder(propertyConversionMethod: PropertyConversionMethod): PostgrestRequestBuilder(propertyConversionMethod) {
11+
open class InsertRequestBuilder(propertyConversionMethod: PropertyConversionMethod, columnRegistry: ColumnRegistry): PostgrestRequestBuilder(propertyConversionMethod, columnRegistry) {
1112

1213
/**
1314
* Make missing fields default to `null`.

Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/RpcRequestBuilder.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.github.jan.supabase.postgrest.query.request
22

3+
import io.github.jan.supabase.postgrest.ColumnRegistry
34
import io.github.jan.supabase.postgrest.Postgrest
45
import io.github.jan.supabase.postgrest.PropertyConversionMethod
56
import io.github.jan.supabase.postgrest.RpcMethod
@@ -8,7 +9,11 @@ import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder
89
/**
910
* Request builder for [Postgrest.rpc]
1011
*/
11-
class RpcRequestBuilder(defaultSchema: String, propertyConversionMethod: PropertyConversionMethod): PostgrestRequestBuilder(propertyConversionMethod) {
12+
class RpcRequestBuilder(
13+
defaultSchema: String,
14+
propertyConversionMethod: PropertyConversionMethod,
15+
columnRegistry: ColumnRegistry
16+
): PostgrestRequestBuilder(propertyConversionMethod, columnRegistry) {
1217

1318
/**
1419
* The HTTP method to use. Default is POST

Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/SelectRequestBuilder.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package io.github.jan.supabase.postgrest.query.request
22

3+
import io.github.jan.supabase.postgrest.ColumnRegistry
34
import io.github.jan.supabase.postgrest.PropertyConversionMethod
45
import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder
56
import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder
67

78
/**
89
* Request builder for [PostgrestQueryBuilder.select]
910
*/
10-
class SelectRequestBuilder(propertyConversionMethod: PropertyConversionMethod): PostgrestRequestBuilder(propertyConversionMethod) {
11+
class SelectRequestBuilder(
12+
propertyConversionMethod: PropertyConversionMethod,
13+
columnRegistry: ColumnRegistry
14+
): PostgrestRequestBuilder(propertyConversionMethod, columnRegistry) {
1115

1216
/**
1317
* If true, no body will be returned. Useful when using count.

0 commit comments

Comments
 (0)