This commit is contained in:
Kevin Grüneberg
2021-01-30 23:56:38 +01:00
parent f62a93efad
commit 121f3a54bf
10 changed files with 349 additions and 99 deletions

View File

@@ -6,11 +6,21 @@ import io.supabase.postgrest.http.PostgrestHttpClient
import io.supabase.postgrest.json.PostgrestJsonConverter
import java.net.URI
/**
* Main client and entry point for using PostgREST client.
*
* @param[uri] URL of the PostgREST endpoint.
* @param[headers] Custom headers.
* @param[schema] Postgres schema to switch to.
* @param[httpClient] Implementation of the [PostgrestHttpClient] interface.
* @param[jsonConverter] Implementation of the [PostgrestJsonConverter] interface
*/
open class PostgrestClient(
private val uri: URI,
private val postgrestHttpClient: PostgrestHttpClient,
private val jsonConverter: PostgrestJsonConverter,
private val defaultHeaders: Map<String, String> = emptyMap()
private val headers: Map<String, String> = emptyMap(),
private val schema: String? = null,
private val httpClient: PostgrestHttpClient,
private val jsonConverter: PostgrestJsonConverter
) {
/**
@@ -20,13 +30,19 @@ open class PostgrestClient(
*/
fun <T : Any> from(table: String): PostgrestQueryBuilder<T> {
val uri = URI("$uri/$table")
return PostgrestQueryBuilder(uri, postgrestHttpClient, jsonConverter, defaultHeaders)
return PostgrestQueryBuilder(uri, httpClient, jsonConverter, headers, schema)
}
/**
* Perform a stored procedure call.
*
* @param[fn] The function name to call.
* @param[params] The parameters to pass to the function call.
*/
fun <T : Any> rpc(fn: String, params: Any?): PostgrestBuilder<T> {
val uri = URI("${this.uri}/rpc/${fn}")
return PostgrestQueryBuilder<T>(uri, postgrestHttpClient, jsonConverter, defaultHeaders).rpc(params)
return PostgrestQueryBuilder<T>(uri, httpClient, jsonConverter, headers, schema).rpc(params)
}
}

View File

@@ -13,16 +13,22 @@ val jsonConverter = PostgrestJsonConverterJackson()
* The default client uses Apache HTTP client 5.x and Jackson FasterXML for DTO conversion.
*
* If you want to customize, implement [PostgrestHttpClient] and [PostgrestJsonConverter].
*
* @param[uri] URL of the PostgREST endpoint.
* @param[headers] Custom headers.
* @param[schema] Postgres schema to switch to.
*/
class PostgrestDefaultClient(
uri: URI,
defaultHeaders: Map<String, String> = emptyMap()
headers: Map<String, String> = emptyMap(),
schema: String? = null
) : PostgrestClient(
postgrestHttpClient = PostgrestHttpClientApache(
uri = uri,
headers = headers,
schema = schema,
httpClient = PostgrestHttpClientApache(
httpClient = HttpClients.createDefault(),
postgrestJsonConverter = jsonConverter
),
jsonConverter = jsonConverter,
defaultHeaders = defaultHeaders,
uri = uri
jsonConverter = jsonConverter
)

View File

@@ -12,6 +12,7 @@ open class PostgrestBuilder<T : Any> {
val jsonConverter: PostgrestJsonConverter
private val url: URI
private var schema: String? = null
private var headers: MutableMap<String, String> = mutableMapOf()
private var method: Method? = null
private var body: Any? = null
@@ -24,14 +25,16 @@ open class PostgrestBuilder<T : Any> {
this.url = builder.url
this.body = builder.body
this.jsonConverter = builder.jsonConverter
this.schema = schema
}
constructor(url: URI, httpClient: PostgrestHttpClient, jsonConverter: PostgrestJsonConverter, defaultHeaders: Map<String, String>) {
constructor(url: URI, httpClient: PostgrestHttpClient, jsonConverter: PostgrestJsonConverter, headers: Map<String, String>, schema: String?) {
this.url = url
this.httpClient = httpClient
this.jsonConverter = jsonConverter
this.schema = schema
defaultHeaders.forEach { (name, value) -> setHeader(name, value) }
headers.forEach { (name, value) -> setHeader(name, value) }
}
protected fun setHeader(name: String, value: String) {
@@ -53,6 +56,19 @@ open class PostgrestBuilder<T : Any> {
fun execute(): PostgrestHttpResponse {
checkNotNull(method) { "Method cannot be null" }
// https://postgrest.org/en/stable/api.html#switching-schemas
if (schema != null) {
// skip
if (this.method in listOf(Method.GET, Method.HEAD)) {
setHeader("Accept-Profile", this.schema!!)
} else {
setHeader("Content-Profile", this.schema!!)
}
if (this.method != Method.GET && this.method != Method.HEAD) {
setHeader("Content-Type", "application/json")
}
}
val uriParams = searchParams.entries.joinToString("&") { (name, value) -> "$name=$value" }
val uriWithParams = URI("${this.url}?${uriParams}")

View File

@@ -7,158 +7,227 @@ class PostgrestFilterBuilder<T : Any>(builder: PostgrestBuilder<T>) : PostgrestT
/**
* Finds all rows which doesn't satisfy the filter.
*
* @param column The column to filter on.
* @param operator The operator to filter with.
* @param value The value to filter with.
* @param[column] The column to filter on.
* @param[operator] The operator to filter with.
* @param[value] The value to filter with.
*/
fun not(column: String, operator: FilterOperator, value: Any): PostgrestFilterBuilder<T> {
setSearchParam(column, "not.${operator.identifier}.${value}")
return this
}
/**
* Finds all rows which doesn't satisfy the filter.
*
* @param[column] The column to filter on.
* @param[operator] The operator to filter with.
* @param[value] The value to filter with.
*/
fun not(column: KProperty1<T, Any>, operator: FilterOperator, value: Any): PostgrestFilterBuilder<T> {
return not(column.name, operator, value)
}
/**
* Finds all rows satisfying at least one of the filters.
* Finds all rows satisfying at least one of the [filters].
*
* @param filters The filters to use, separated by commas.
* @param[filters] The filters to use, separated by commas.
*/
fun or(filters: String): PostgrestFilterBuilder<T> {
setSearchParam("or", "(${filters})")
return this
}
/**
* Finds all rows whose value on the stated [column] exactly matches the
* specified [value].
*
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun eq(column: KProperty1<T, Any>, value: Any): PostgrestFilterBuilder<T> {
return eq(column.name, value)
}
/**
* Finds all rows whose value on the stated "column" exactly matches the
* specified "value".
* Finds all rows whose value on the stated [column] exactly matches the
* specified [value].
*
* @param column The column to filter on.
* @param value The value to filter with.
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun eq(column: String, value: Any): PostgrestFilterBuilder<T> {
setSearchParam(column, "eq.${value}")
return this
}
/**
* Finds all rows whose value on the stated [column] doesn't match the
* specified [value].
*
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun neq(column: String, value: Any): PostgrestFilterBuilder<T> {
setSearchParam(column, "neq.${value}")
return this
}
/**
* Finds all rows whose value on the stated "column" doesn't match the
* specified "value".
* Finds all rows whose value on the stated [column] doesn't match the
* specified [value].
*
* @param column The column to filter on.
* @param value The value to filter with.
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun neq(column: KProperty1<T, Any>, value: Any): PostgrestFilterBuilder<T> {
return neq(column.name, value)
}
/**
* Finds all rows whose value on the stated [column] is greater than the
* specified [value].
*
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun gt(column: String, value: Any): PostgrestFilterBuilder<T> {
setSearchParam(column, "gt.${value}")
return this
}
/**
* Finds all rows whose value on the stated "column" is greater than the
* specified "value".
* Finds all rows whose value on the stated [column] is greater than the
* specified [value].
*
* @param column The column to filter on.
* @param value The value to filter with.
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun gt(column: KProperty1<T, Any>, value: Any): PostgrestFilterBuilder<T> {
return gt(column.name, value)
}
/**
* Finds all rows whose value on the stated [column] is greater than or
* equal to the specified [value].
*
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun gte(column: String, value: Any): PostgrestFilterBuilder<T> {
setSearchParam(column, "gte.${value}")
return this
}
/**
* Finds all rows whose value on the stated "column" is greater than or
* equal to the specified "value".
* Finds all rows whose value on the stated [column] is greater than or
* equal to the specified [value].
*
* @param column The column to filter on.
* @param value The value to filter with.
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun gte(column: KProperty1<T, Any>, value: Any): PostgrestFilterBuilder<T> {
return gte(column.name, value)
}
/**
* Finds all rows whose value on the stated [column] is less than the
* specified [value].
*
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun lt(column: String, value: Any): PostgrestFilterBuilder<T> {
setSearchParam(column, "lt.${value}")
return this
}
/**
* Finds all rows whose value on the stated "column" is less than the
* specified "value".
* Finds all rows whose value on the stated [column] is less than the
* specified [value].
*
* @param column The column to filter on.
* @param value The value to filter with.
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun lt(column: KProperty1<T, Any>, value: Any): PostgrestFilterBuilder<T> {
return lt(column.name, value)
}
/**
* Finds all rows whose value on the stated [column] is less than or equal
* to the specified [value].
*
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun lte(column: String, value: Any): PostgrestFilterBuilder<T> {
setSearchParam(column, "lte.${value}")
return this
}
/**
* Finds all rows whose value on the stated "column" is less than or equal
* to the specified "value".
* Finds all rows whose value on the stated [column] is less than or equal
* to the specified [value].
*
* @param column The column to filter on.
* @param value The value to filter with.
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun lte(column: KProperty1<T, Any>, value: Any): PostgrestFilterBuilder<T> {
return lte(column.name, value)
}
/**
* Finds all rows whose value in the stated [column] matches the supplied
* [pattern] (case sensitive).
*
* @param[column] The column to filter on.
* @param[pattern] The pattern to filter with.
*/
fun like(column: String, pattern: String): PostgrestFilterBuilder<T> {
setSearchParam(column, "like.${pattern}")
return this
}
/**
* Finds all rows whose value in the stated "column" matches the supplied
* "pattern" (case sensitive).
* Finds all rows whose value in the stated [column] matches the supplied
* [pattern] (case sensitive).
*
* @param column The column to filter on.
* @param pattern The pattern to filter with.
* @param[column] The column to filter on.
* @param[pattern] The pattern to filter with.
*/
fun like(column: KProperty1<T, Any>, pattern: String): PostgrestFilterBuilder<T> {
return like(column.name, pattern)
}
/**
* Finds all rows whose value in the stated [column] matches the supplied
* [pattern] (case insensitive).
*
* @param[column] The column to filter on.
* @param[pattern] The pattern to filter with.
*/
fun ilike(column: String, pattern: String): PostgrestFilterBuilder<T> {
setSearchParam(column, "ilike.${pattern}")
return this
}
/**
* Finds all rows whose value in the stated "column" matches the supplied
* "pattern" (case insensitive).
* Finds all rows whose value in the stated [column] matches the supplied
* [pattern] (case insensitive).
*
* @param column The column to filter on.
* @param pattern The pattern to filter with.
* @param[column] The column to filter on.
* @param[pattern] The pattern to filter with.
*/
fun ilike(column: KProperty1<T, Any>, pattern: String): PostgrestFilterBuilder<T> {
return ilike(column.name, pattern)
}
/**
* A check for exact equality (null, true, false), finds all rows whose
* value on the stated [column] exactly match the specified [value].
*
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun `is`(column: String, value: Boolean?): PostgrestFilterBuilder<T> {
setSearchParam(column, "is.${value}")
return this
@@ -166,26 +235,33 @@ class PostgrestFilterBuilder<T : Any>(builder: PostgrestBuilder<T>) : PostgrestT
/**
* A check for exact equality (null, true, false), finds all rows whose
* value on the stated "column" exactly match the specified "value".
* value on the stated [column] exactly match the specified [value].
*
* @param column The column to filter on.
* @param value The value to filter with.
* @param[column] The column to filter on.
* @param[value] The value to filter with.
*/
fun `is`(column: KProperty1<T, Any>, value: Boolean?): PostgrestFilterBuilder<T> {
return `is`(column.name, value)
}
/**
* Finds all rows whose value on the stated [column] is found on the
* specified [values].
*
* @param[column] The column to filter on.
* @param[values] The values to filter with.
*/
fun `in`(column: String, values: List<Any>): PostgrestFilterBuilder<T> {
setSearchParam(column, "in.(${cleanFilterArray(values)})")
return this
}
/**
* Finds all rows whose value on the stated "column" is found on the
* specified "values".
* Finds all rows whose value on the stated [column] is found on the
* specified [values].
*
* @param column The column to filter on.
* @param values The values to filter with.
* @param[column] The column to filter on.
* @param[values] The values to filter with.
*/
fun `in`(column: KProperty1<T, Any>, values: List<Any>): PostgrestFilterBuilder<T> {
return `in`(column.name, values)
@@ -195,86 +271,130 @@ class PostgrestFilterBuilder<T : Any>(builder: PostgrestBuilder<T>) : PostgrestT
return values.joinToString(",") { s -> """"$s"""" }
}
/**
* Finds all rows whose range value on the stated [column] is strictly to the
* left of the specified [range].
*
* @param[column] The column to filter on.
* @param[range] The range to filter with.
*/
fun rangeLt(column: String, range: String): PostgrestFilterBuilder<T> {
setSearchParam(column, "sl.${range}")
return this
}
/**
* Finds all rows whose range value on the stated "column" is strictly to the
* left of the specified "range".
* Finds all rows whose range value on the stated [column] is strictly to the
* left of the specified [range].
*
* @param column The column to filter on.
* @param range The range to filter with.
* @param[column] The column to filter on.
* @param[range] The range to filter with.
*/
fun rangeLt(column: KProperty1<T, Any>, range: String): PostgrestFilterBuilder<T> {
return rangeLt(column.name, range)
}
/**
* Finds all rows whose range value on the stated [column] is strictly to
* the right of the specified [range].
*
* @param[column] The column to filter on.
* @param[range] The range to filter with.
*/
fun rangeGt(column: String, range: String): PostgrestFilterBuilder<T> {
setSearchParam(column, "sr.${range}")
return this
}
/**
* Finds all rows whose range value on the stated "column" is strictly to
* the right of the specified "range".
* Finds all rows whose range value on the stated [column] is strictly to
* the right of the specified [range].
*
* @param column The column to filter on.
* @param range The range to filter with.
* @param[column] The column to filter on.
* @param[range] The range to filter with.
*/
fun rangeGt(column: KProperty1<T, Any>, range: String): PostgrestFilterBuilder<T> {
return rangeGt(column.name, range)
}
/**
* Finds all rows whose range value on the stated [column] does not extend
* to the left of the specified [range].
*
* @param[column] The column to filter on.
* @param[range] The range to filter with.
*/
fun rangeGte(column: String, range: String): PostgrestFilterBuilder<T> {
setSearchParam(column, "nxl.${range}")
return this
}
/**
* Finds all rows whose range value on the stated "column" does not extend
* to the left of the specified "range".
* Finds all rows whose range value on the stated [column] does not extend
* to the left of the specified [range].
*
* @param column The column to filter on.
* @param range The range to filter with.
* @param[column] The column to filter on.
* @param[range] The range to filter with.
*/
fun rangeGte(column: KProperty1<T, Any>, range: String): PostgrestFilterBuilder<T> {
return rangeGte(column.name, range)
}
/**
* Finds all rows whose range value on the stated [column] does not extend
* to the right of the specified [range].
*
* @param[column] The column to filter on.
* @param[range] The range to filter with.
*/
fun rangeLte(column: String, range: String): PostgrestFilterBuilder<T> {
setSearchParam(column, "nxr.${range}")
return this
}
/**
* Finds all rows whose range value on the stated "column" does not extend
* to the right of the specified "range".
* Finds all rows whose range value on the stated [column] does not extend
* to the right of the specified [range].
*
* @param column The column to filter on.
* @param range The range to filter with.
* @param[column] The column to filter on.
* @param[range] The range to filter with.
*/
fun rangeLte(column: KProperty1<T, Any>, range: String): PostgrestFilterBuilder<T> {
return rangeLte(column.name, range)
}
/**
* Finds all rows whose range value on the stated [column] is adjacent to
* the specified [range].
*
* @param[column] The column to filter on.
* @param[range] The range to filter with.
*/
fun adjacent(column: String, range: String): PostgrestFilterBuilder<T> {
setSearchParam(column, "adj.${range}")
return this
}
/**
* Finds all rows whose range value on the stated "column" is adjacent to
* the specified "range".
* Finds all rows whose range value on the stated [column] is adjacent to
* the specified [range].
*
* @param column The column to filter on.
* @param range The range to filter with.
* @param[column] The column to filter on.
* @param[range] The range to filter with.
*/
fun adjacent(column: KProperty1<T, Any>, range: String): PostgrestFilterBuilder<T> {
return adjacent(column.name, range)
}
/**
* Finds all rows whose tsvector value on the stated [column] matches
* [query] using the given [textSearchType].
*
* @param [column] The column to filter on.
* @param [query] The Postgres tsquery string to filter with.
* @param [textSearchType] The Postgres text search type.
* @param [config] The text search configuration to use.
*/
fun textSearch(column: String, query: String, textSearchType: TextSearchType, config: String? = null): PostgrestFilterBuilder<T> {
val configPart = if (config === null) "" else "(${config})"
setSearchParam(column, "${textSearchType.identifier}${configPart}.${query}")
@@ -285,22 +405,34 @@ class PostgrestFilterBuilder<T : Any>(builder: PostgrestBuilder<T>) : PostgrestT
return textSearch(column.name, query, textSearchType, config)
}
/**
* Finds all rows whose [column] satisfies the filter.
*
* @param[column] The column to filter on.
* @param[operator] The operator to filter with.
* @param[value] The value to filter with.
*/
fun filter(column: String, operator: FilterOperator, value: Any): PostgrestFilterBuilder<T> {
setSearchParam(column, "${operator.identifier}.${value}")
return this
}
/**
* Finds all rows whose "column" satisfies the filter.
* Finds all rows whose [column] satisfies the filter.
*
* @param column The column to filter on.
* @param operator The operator to filter with.
* @param value The value to filter with.
* @param[column] The column to filter on.
* @param[operator] The operator to filter with.
* @param[value] The value to filter with.
*/
fun filter(column: KProperty1<T, Any>, operator: FilterOperator, value: Any): PostgrestFilterBuilder<T> {
return filter(column.name, operator, value)
}
/**
* Finds all rows whose columns match the specified [query] object.
*
* @param[query] The object to filter with, with column names as keys mapped to their filter values.
*/
fun match(query: Map<String, Any>): PostgrestFilterBuilder<T> {
query.entries.forEach { (name, value) -> setSearchParam(name, "eq.$value") }
return this

View File

@@ -5,8 +5,8 @@ import io.supabase.postgrest.json.PostgrestJsonConverter
import org.apache.hc.core5.http.Method
import java.net.URI
class PostgrestQueryBuilder<T : Any>(url: URI, postgrestHttpClient: PostgrestHttpClient, jsonConverter: PostgrestJsonConverter, defaultHeaders: Map<String, String>)
: PostgrestBuilder<T>(url, postgrestHttpClient, jsonConverter, defaultHeaders) {
class PostgrestQueryBuilder<T : Any>(url: URI, postgrestHttpClient: PostgrestHttpClient, jsonConverter: PostgrestJsonConverter, defaultHeaders: Map<String, String>, schema: String?)
: PostgrestBuilder<T>(url, postgrestHttpClient, jsonConverter, defaultHeaders, schema) {
/**
* Performs vertical filtering with SELECT.
@@ -37,14 +37,22 @@ class PostgrestQueryBuilder<T : Any>(url: URI, postgrestHttpClient: PostgrestHtt
return PostgrestFilterBuilder(this)
}
fun insert(value: List<T>, upsert: Boolean = false, onConflict: String? = null, returning: Returning = Returning.REPRESENTATION, count: Count?): PostgrestFilterBuilder<T> {
/**
* Performs an INSERT into the table.
*
* @param[values] The values to insert.
* @param[upsert] If `true`, performs an UPSERT.
* @param[onConflict] By specifying the `on_conflict` query parameter, you can make UPSERT work on a column(s) that has a UNIQUE constraint.
* @param[returning] By default the new record is returned. Set this to 'minimal' if you don't need this value.
*/
fun insert(values: List<T>, upsert: Boolean = false, onConflict: String? = null, returning: Returning = Returning.REPRESENTATION, count: Count? = null): PostgrestFilterBuilder<T> {
setMethod(Method.POST)
val preferHeaders = mutableListOf<String>("return=${returning.identifier}")
if (upsert) preferHeaders.add("resolution=merge-duplicates")
if (upsert && onConflict != null) setSearchParam("on_conflict", onConflict)
setBody(value)
setBody(values)
if (count != null) {
preferHeaders.add("count=${count}")
@@ -55,14 +63,30 @@ class PostgrestQueryBuilder<T : Any>(url: URI, postgrestHttpClient: PostgrestHtt
return PostgrestFilterBuilder(this)
}
fun insert(value: T, upsert: Boolean = false, onConflict: String? = null, returning: Returning = Returning.REPRESENTATION, count: Count?): PostgrestFilterBuilder<T> {
/**
* Performs an INSERT into the table.
*
* @param[value] The value to insert.
* @param[upsert] If `true`, performs an UPSERT.
* @param[onConflict] By specifying the `on_conflict` query parameter, you can make UPSERT work on a column(s) that has a UNIQUE constraint.
* @param[returning] By default the new record is returned. Set this to 'minimal' if you don't need this value.
*/
fun insert(value: T, upsert: Boolean = false, onConflict: String? = null, returning: Returning = Returning.REPRESENTATION, count: Count? = null): PostgrestFilterBuilder<T> {
return insert(listOf(value), upsert, onConflict, returning, count)
}
/**
* Performs an UPDATE on the table.
*
* @param[value] The values to update.
* @param[returning] By default the updated record is returned. Set this to 'minimal' if you don't need this value.
*/
fun update(value: Any, returning: Returning = Returning.REPRESENTATION, count: Count?): PostgrestFilterBuilder<T> {
setMethod(Method.PATCH)
val prefersHeaders = mutableListOf("return=${returning.identifier}")
setBody(value)
val prefersHeaders = mutableListOf("return=${returning.identifier}")
if (count != null) {
prefersHeaders.add("count=${count}")
}
@@ -71,6 +95,11 @@ class PostgrestQueryBuilder<T : Any>(url: URI, postgrestHttpClient: PostgrestHtt
return PostgrestFilterBuilder(this)
}
/**
* Performs a DELETE on the table.
*
* @param[returning] If `true`, return the deleted row(s) in the response.
*/
fun delete(returning: Returning = Returning.REPRESENTATION, count: Count?): PostgrestFilterBuilder<T> {
setMethod(Method.DELETE)
@@ -83,7 +112,7 @@ class PostgrestQueryBuilder<T : Any>(url: URI, postgrestHttpClient: PostgrestHtt
return PostgrestFilterBuilder(this)
}
fun rpc(params: Any?): PostgrestBuilder<T> {
internal fun rpc(params: Any?): PostgrestBuilder<T> {
setMethod(Method.POST)
setBody(params)
return this

View File

@@ -1,9 +1,14 @@
package io.supabase.postgrest.builder
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
open class PostgrestTransformBuilder<T : Any>(builder: PostgrestBuilder<T>) : PostgrestBuilder<T>(builder) {
/**
* Performs vertical filtering with SELECT.
*
* @param[columns] The columns to retrieve, separated by commas.
*/
fun select(columns: String = "*"): PostgrestTransformBuilder<T> {
val cleanedColumns = cleanColumns(columns)
setSearchParam("select", cleanedColumns)
@@ -11,7 +16,15 @@ open class PostgrestTransformBuilder<T : Any>(builder: PostgrestBuilder<T>) : Po
return this
}
fun order(column: KProperty<T>, ascending: Boolean = true, nullsFirst: Boolean = false, foreignTable: String? = null): PostgrestTransformBuilder<T> {
/**
* Orders the result with the specified [column].
*
* @param[column] The column to order on.
* @param[ascending] If `true`, the result will be in ascending order.
* @param[nullsFirst] If `true`, `null`s appear first.
* @param[foreignTable] The foreign table to use (if `column` is a foreign column).
*/
fun order(column: String, ascending: Boolean = true, nullsFirst: Boolean = false, foreignTable: String? = null): PostgrestTransformBuilder<T> {
val key = if (foreignTable == null) "order" else """"$foreignTable".order"""
setSearchParam(
key,
@@ -21,6 +34,24 @@ open class PostgrestTransformBuilder<T : Any>(builder: PostgrestBuilder<T>) : Po
return this
}
/**
* Orders the result with the specified [column].
*
* @param[column] The column to order on.
* @param[ascending] If `true`, the result will be in ascending order.
* @param[nullsFirst] If `true`, `null`s appear first.
* @param[foreignTable] The foreign table to use (if `column` is a foreign column).
*/
fun order(column: KProperty1<T, Any>, ascending: Boolean = true, nullsFirst: Boolean = false, foreignTable: String? = null): PostgrestTransformBuilder<T> {
return order(column.name, ascending, nullsFirst, foreignTable)
}
/**
* Limits the result with the specified [count].
*
* @param[count] The maximum no. of rows to limit to.
* @param[foreignTable] The foreign table to use (for foreign columns).
*/
fun limit(count: Long, foreignTable: String? = null): PostgrestTransformBuilder<T> {
val key = if (foreignTable == null) "limit" else """"$foreignTable".limit"""
setSearchParam(key, count.toString())
@@ -28,6 +59,13 @@ open class PostgrestTransformBuilder<T : Any>(builder: PostgrestBuilder<T>) : Po
return this
}
/**
* Limits the result to rows within the specified range, inclusive.
*
* @param[from] The starting index from which to limit the result, inclusive.
* @param[to] The last index to which to limit the result, inclusive.
* @param[foreignTable] The foreign table to use (for foreign columns).
*/
fun range(from: Long, to: Long, foreignTable: String? = null): PostgrestTransformBuilder<T> {
val keyOffset = if (foreignTable == null) "offset" else """"$foreignTable".offset"""
val keyLimit = if (foreignTable == null) "limit" else """"$foreignTable".limit"""
@@ -39,6 +77,10 @@ open class PostgrestTransformBuilder<T : Any>(builder: PostgrestBuilder<T>) : Po
return this
}
/**
* Retrieves only one row from the result. Result must be one row (e.g. using `limit`),
* otherwise this will result in an error.
*/
fun single(): PostgrestTransformBuilder<T> {
setHeader(org.apache.hc.core5.http.HttpHeaders.ACCEPT, "application/vnd.pgrst.object+json")

View File

@@ -6,6 +6,6 @@ package io.supabase.postgrest.http
* If you implement your custom PostgrestHttpClient, you need to handle exceptions on your own.
*
* @property[status] HTTP status code
* @property[httpBody] Response body as [String] if available
* @property[data] Response body as [String] if available
*/
class PostgrestHttpException(val status: Int, val httpBody: String?) : RuntimeException("Unexpected response status: $status")
class PostgrestHttpException(val status: Int, val data: String?) : RuntimeException("Unexpected response status: $status")

View File

@@ -24,7 +24,13 @@ interface PostgrestJsonConverter {
*/
fun <T : Any> deserialize(text: String, responseType: Class<T>): T
fun <T : Any> deserializeList(text: String, java: Class<T>): List<T>
/**
* Deserializes a JSON [text] to a list of the corresponding [responseType].
*
* @param[text] The JSON text to convert
* @param[responseType] The response type as Java class
*/
fun <T : Any> deserializeList(text: String, responseType: Class<T>): List<T>
}
inline fun <reified T : Any> PostgrestJsonConverter.deserialize(content: String): T = deserialize(content, T::class.java)

View File

@@ -25,9 +25,9 @@ class PostgrestJsonConverterJackson : PostgrestJsonConverter {
return objectMapper.readValue(text, responseType)
}
override fun <T : Any> deserializeList(text: String, java: Class<T>): List<T> {
override fun <T : Any> deserializeList(text: String, responseType: Class<T>): List<T> {
val javaType = objectMapper.typeFactory
.constructCollectionType(MutableList::class.java, java)
.constructCollectionType(MutableList::class.java, responseType)
return objectMapper.readValue(text, javaType)
}

View File

@@ -6,15 +6,18 @@ import java.net.URI
class PostgresClientTest {
@Test
fun foo() {
fun `if it looks stupid but works it aint stupid`() {
val client = PostgrestDefaultClient(
uri = URI("https://eyimuvrqyphojiqwapfv.supabase.co/rest/v1"),
defaultHeaders = mapOf("apiKey" to "xyz")
headers = mapOf("apiKey" to "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYxMTQ0NTk4OCwiZXhwIjoxOTI3MDIxOTg4fQ.OW1kc8pmB7Q9EO9iUSdg86cZoyx_3rLoQUJrQg35Bvs")
)
val a = client.from<Foo>("foo")
val dataAsList = client.from<Foo>("foo")
.select()
.execute()
.eq(Foo::text, "asdasd")
.executeAndGetList<Foo>()
println(dataAsList)
}
}