Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
g12i committed Apr 18, 2024
2 parents d22e54e + 03a811d commit 4c78317
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 25 deletions.
40 changes: 19 additions & 21 deletions src/PostgrestClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,9 @@ export default class PostgrestClient<
* `"estimated"`: Uses exact count for low numbers and planned count for high
* numbers.
*/
rpc<
FunctionName extends string & keyof Schema['Functions'],
Function_ extends Schema['Functions'][FunctionName]
>(
fn: FunctionName,
args: Function_['Args'] = {},
rpc<FnName extends string & keyof Schema['Functions'], Fn extends Schema['Functions'][FnName]>(
fn: FnName,
args: Fn['Args'] = {},
{
head = false,
get = false,
Expand All @@ -138,26 +135,27 @@ export default class PostgrestClient<
} = {}
): PostgrestFilterBuilder<
Schema,
Function_['Returns'] extends any[]
? Function_['Returns'][number] extends Record<string, unknown>
? Function_['Returns'][number]
Fn['Returns'] extends any[]
? Fn['Returns'][number] extends Record<string, unknown>
? Fn['Returns'][number]
: never
: never,
Function_['Returns']
Fn['Returns']
> {
let method: 'HEAD' | 'GET' | 'POST'
const url = new URL(`${this.url}/rpc/${fn}`)
let body: unknown | undefined
if (head) {
method = 'HEAD'
Object.entries(args).forEach(([name, value]) => {
url.searchParams.append(name, `${value}`)
})
} else if (get) {
method = 'GET'
Object.entries(args).forEach(([name, value]) => {
url.searchParams.append(name, `${value}`)
})
if (head || get) {
method = head ? 'HEAD' : 'GET'
Object.entries(args)
// params with undefined value needs to be filtered out, otherwise it'll
// show up as `?param=undefined`
.filter(([_, value]) => value !== undefined)
// array values need special syntax
.map(([name, value]) => [name, Array.isArray(value) ? `{${value.join(',')}}` : `${value}`])
.forEach(([name, value]) => {
url.searchParams.append(name, value)
})
} else {
method = 'POST'
body = args
Expand All @@ -176,6 +174,6 @@ export default class PostgrestClient<
body,
fetch: this.fetch,
allowEmpty: false,
} as unknown as PostgrestBuilder<Function_['Returns']>)
} as unknown as PostgrestBuilder<Fn['Returns']>)
}
}
34 changes: 34 additions & 0 deletions test/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,40 @@ test('rpc with get:true, count:exact', async () => {
`)
})

test('rpc with get:true, optional param', async () => {
const res = await postgrest.rpc(
'function_with_optional_param',
{ param: undefined },
{ get: true }
)
expect(res).toMatchInlineSnapshot(`
Object {
"count": null,
"data": "",
"error": null,
"status": 200,
"statusText": "OK",
}
`)
})

test('rpc with get:true, array param', async () => {
const res = await postgrest.rpc(
'function_with_array_param',
{ param: ['00000000-0000-0000-0000-000000000000'] },
{ get: true }
)
expect(res).toMatchInlineSnapshot(`
Object {
"count": null,
"data": null,
"error": null,
"status": 204,
"statusText": "No Content",
}
`)
})

test('rpc with dynamic schema', async () => {
const res = await postgrest.schema('personal').rpc('get_status', { name_param: 'kiwicopple' })
expect(res).toMatchInlineSnapshot(`
Expand Down
8 changes: 8 additions & 0 deletions test/db/00-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,11 @@ CREATE FUNCTION personal.get_status(name_param text)
RETURNS user_status AS $$
SELECT status from users WHERE username=name_param;
$$ LANGUAGE SQL IMMUTABLE;

create function public.function_with_optional_param(param text default '')
returns text as $$
select param;
$$ language sql immutable;

create function public.function_with_array_param(param uuid[])
returns void as '' language sql immutable;
98 changes: 94 additions & 4 deletions test/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]

export interface Database {
export type Database = {
personal: {
Tables: {
users: {
Expand Down Expand Up @@ -112,25 +112,29 @@ export interface Database {
{
foreignKeyName: 'messages_channel_id_fkey'
columns: ['channel_id']
isOneToOne: false
referencedRelation: 'channels'
referencedColumns: ['id']
},
{
foreignKeyName: 'messages_username_fkey'
columns: ['username']
referencedRelation: 'users'
isOneToOne: false
referencedRelation: 'non_updatable_view'
referencedColumns: ['username']
},
{
foreignKeyName: 'messages_username_fkey'
columns: ['username']
referencedRelation: 'non_updatable_view'
isOneToOne: false
referencedRelation: 'updatable_view'
referencedColumns: ['username']
},
{
foreignKeyName: 'messages_username_fkey'
columns: ['username']
referencedRelation: 'updatable_view'
isOneToOne: false
referencedRelation: 'users'
referencedColumns: ['username']
}
]
Expand Down Expand Up @@ -202,6 +206,18 @@ export interface Database {
}
}
Functions: {
function_with_array_param: {
Args: {
param: string[]
}
Returns: undefined
}
function_with_optional_param: {
Args: {
param?: string
}
Returns: string
}
get_status: {
Args: {
name_param: string
Expand Down Expand Up @@ -236,3 +252,77 @@ export interface Database {
}
}
}

type PublicSchema = Database[Extract<keyof Database, 'public'>]

export type Tables<
PublicTableNameOrOptions extends
| keyof (PublicSchema['Tables'] & PublicSchema['Views'])
| { schema: keyof Database },
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
? keyof (Database[PublicTableNameOrOptions['schema']]['Tables'] &
Database[PublicTableNameOrOptions['schema']]['Views'])
: never = never
> = PublicTableNameOrOptions extends { schema: keyof Database }
? (Database[PublicTableNameOrOptions['schema']]['Tables'] &
Database[PublicTableNameOrOptions['schema']]['Views'])[TableName] extends {
Row: infer R
}
? R
: never
: PublicTableNameOrOptions extends keyof (PublicSchema['Tables'] & PublicSchema['Views'])
? (PublicSchema['Tables'] & PublicSchema['Views'])[PublicTableNameOrOptions] extends {
Row: infer R
}
? R
: never
: never

export type TablesInsert<
PublicTableNameOrOptions extends keyof PublicSchema['Tables'] | { schema: keyof Database },
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
? keyof Database[PublicTableNameOrOptions['schema']]['Tables']
: never = never
> = PublicTableNameOrOptions extends { schema: keyof Database }
? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends {
Insert: infer I
}
? I
: never
: PublicTableNameOrOptions extends keyof PublicSchema['Tables']
? PublicSchema['Tables'][PublicTableNameOrOptions] extends {
Insert: infer I
}
? I
: never
: never

export type TablesUpdate<
PublicTableNameOrOptions extends keyof PublicSchema['Tables'] | { schema: keyof Database },
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
? keyof Database[PublicTableNameOrOptions['schema']]['Tables']
: never = never
> = PublicTableNameOrOptions extends { schema: keyof Database }
? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends {
Update: infer U
}
? U
: never
: PublicTableNameOrOptions extends keyof PublicSchema['Tables']
? PublicSchema['Tables'][PublicTableNameOrOptions] extends {
Update: infer U
}
? U
: never
: never

export type Enums<
PublicEnumNameOrOptions extends keyof PublicSchema['Enums'] | { schema: keyof Database },
EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database }
? keyof Database[PublicEnumNameOrOptions['schema']]['Enums']
: never = never
> = PublicEnumNameOrOptions extends { schema: keyof Database }
? Database[PublicEnumNameOrOptions['schema']]['Enums'][EnumName]
: PublicEnumNameOrOptions extends keyof PublicSchema['Enums']
? PublicSchema['Enums'][PublicEnumNameOrOptions]
: never

0 comments on commit 4c78317

Please sign in to comment.