Skip to content

Commit a789624

Browse files
committed
wip
1 parent 1508524 commit a789624

16 files changed

+2848
-107
lines changed

src/PostgrestClient.ts

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,74 @@
11
import PostgrestQueryBuilder from './PostgrestQueryBuilder'
22
import PostgrestFilterBuilder from './PostgrestFilterBuilder'
3-
import { Fetch, GenericSchema, ClientServerOptions, GetGenericDatabaseWithOptions } from './types'
3+
import {
4+
Fetch,
5+
GenericSchema,
6+
ClientServerOptions,
7+
GetGenericDatabaseWithOptions,
8+
GenericFunction,
9+
GenericSetofOption,
10+
} from './types'
11+
import { FindMatchingFunctionByArgs, IsAny } from './select-query-parser/utils'
12+
13+
type ExactMatch<T, S> = [T] extends [S] ? ([S] extends [T] ? true : false) : false
14+
15+
type ExtractExactFunction<Fns, Args> = Fns extends infer F
16+
? F extends GenericFunction
17+
? ExactMatch<F['Args'], Args> extends true
18+
? F
19+
: never
20+
: never
21+
: never
22+
23+
export type GetRpcFunctionFilterBuilderByArgs<
24+
Schema extends GenericSchema,
25+
FnName extends string & keyof Schema['Functions'],
26+
Args
27+
> = {
28+
0: Schema['Functions'][FnName]
29+
// This is here to handle the case where the args is exactly {} and fallback to the empty
30+
// args function definition if there is one in such case
31+
1: [keyof Args] extends [never]
32+
? ExtractExactFunction<Schema['Functions'][FnName], Args>
33+
: // Otherwise, we attempt to match with one of the function definition in the union based
34+
// on the function arguments provided
35+
Args extends GenericFunction['Args']
36+
? FindMatchingFunctionByArgs<Schema['Functions'][FnName], Args>
37+
: any
38+
}[1] extends infer Fn
39+
? // If we are dealing with an non-typed client everything is any
40+
IsAny<Fn> extends true
41+
? { Row: any; Result: any; RelationName: FnName; Relationships: null }
42+
: // Otherwise, we use the arguments based function definition narrowing to get the rigt value
43+
Fn extends GenericFunction
44+
? {
45+
Row: Fn['Returns'] extends any[]
46+
? Fn['Returns'][number] extends Record<string, unknown>
47+
? Fn['Returns'][number]
48+
: never
49+
: Fn['Returns'] extends Record<string, unknown>
50+
? Fn['Returns']
51+
: never
52+
Result: Fn['Returns']
53+
RelationName: Fn['SetofOptions'] extends GenericSetofOption
54+
? Fn['SetofOptions']['to']
55+
: FnName
56+
Relationships: Fn['SetofOptions'] extends GenericSetofOption
57+
? Fn['SetofOptions']['to'] extends keyof Schema['Tables']
58+
? Schema['Tables'][Fn['SetofOptions']['to']]['Relationships']
59+
: Schema['Views'][Fn['SetofOptions']['to']]['Relationships']
60+
: null
61+
}
62+
: // If we failed to find the function by argument, we still pass with any but also add an overridable
63+
Fn extends false
64+
? {
65+
Row: any
66+
Result: { error: true } & "Couldn't infer function definition matching provided arguments"
67+
RelationName: FnName
68+
Relationships: null
69+
}
70+
: never
71+
: never
472

573
/**
674
* PostgREST client.

src/select-query-parser/result.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ export type ProcessEmbeddedResource<
347347
> = ResolveRelationship<Schema, Relationships, Field, CurrentTableOrView> extends infer Resolved
348348
? Resolved extends {
349349
referencedTable: Pick<GenericTable, 'Row' | 'Relationships'>
350-
relation: GenericRelationship & { match: 'refrel' | 'col' | 'fkname' }
350+
relation: GenericRelationship & { match: 'refrel' | 'col' | 'fkname' | 'func' }
351351
direction: string
352352
}
353353
? ProcessEmbeddedResourceResult<ClientOptions, Schema, Resolved, Field, CurrentTableOrView>
@@ -366,7 +366,10 @@ type ProcessEmbeddedResourceResult<
366366
Schema extends GenericSchema,
367367
Resolved extends {
368368
referencedTable: Pick<GenericTable, 'Row' | 'Relationships'>
369-
relation: GenericRelationship & { match: 'refrel' | 'col' | 'fkname' }
369+
relation: GenericRelationship & {
370+
match: 'refrel' | 'col' | 'fkname' | 'func'
371+
isNotNullable?: boolean
372+
}
370373
direction: string
371374
},
372375
Field extends Ast.FieldNode,
@@ -390,7 +393,11 @@ type ProcessEmbeddedResourceResult<
390393
? ProcessedChildren
391394
: ProcessedChildren[]
392395
: Resolved['relation']['isOneToOne'] extends true
393-
? ProcessedChildren | null
396+
? Resolved['relation']['match'] extends 'func'
397+
? Resolved['relation']['isNotNullable'] extends true
398+
? ProcessedChildren
399+
: ProcessedChildren | null
400+
: ProcessedChildren | null
394401
: ProcessedChildren[]
395402
: // If the relation is a self-reference it'll always be considered as reverse relationship
396403
Resolved['relation']['referencedRelation'] extends CurrentTableOrView

src/select-query-parser/utils.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,11 +452,51 @@ export type ResolveForwardRelationship<
452452
from: CurrentTableOrView
453453
type: 'found-by-join-table'
454454
}
455+
: ResolveEmbededFunctionJoinTableRelationship<
456+
Schema,
457+
CurrentTableOrView,
458+
Field['name']
459+
> extends infer FoundEmbededFunctionJoinTableRelation
460+
? FoundEmbededFunctionJoinTableRelation extends GenericSetofOption
461+
? {
462+
referencedTable: TablesAndViews<Schema>[FoundEmbededFunctionJoinTableRelation['to']]
463+
relation: {
464+
foreignKeyName: `${Field['name']}_${CurrentTableOrView}_${FoundEmbededFunctionJoinTableRelation['to']}_forward`
465+
columns: []
466+
isOneToOne: FoundEmbededFunctionJoinTableRelation['isOneToOne'] extends true
467+
? true
468+
: false
469+
referencedColumns: []
470+
referencedRelation: FoundEmbededFunctionJoinTableRelation['to']
471+
} & {
472+
match: 'func'
473+
isNotNullable: FoundEmbededFunctionJoinTableRelation['isNotNullable'] extends true
474+
? true
475+
: false
476+
}
477+
direction: 'forward'
478+
from: CurrentTableOrView
479+
type: 'found-by-embeded-function'
480+
}
481+
: SelectQueryError<`could not find the relation between ${CurrentTableOrView} and ${Field['name']}`>
455482
: SelectQueryError<`could not find the relation between ${CurrentTableOrView} and ${Field['name']}`>
456483
: SelectQueryError<`could not find the relation between ${CurrentTableOrView} and ${Field['name']}`>
457484
: SelectQueryError<`could not find the relation between ${CurrentTableOrView} and ${Field['name']}`>
458485
: SelectQueryError<`could not find the relation between ${CurrentTableOrView} and ${Field['name']}`>
459486

487+
type ResolveEmbededFunctionJoinTableRelationship<
488+
Schema extends GenericSchema,
489+
CurrentTableOrView extends keyof TablesAndViews<Schema> & string,
490+
FieldName extends string
491+
> = FindMatchingFunctionBySetofFrom<
492+
Schema['Functions'][FieldName],
493+
CurrentTableOrView
494+
> extends infer Fn
495+
? Fn extends GenericFunction
496+
? Fn['SetofOptions']
497+
: false
498+
: false
499+
460500
/**
461501
* Given a CurrentTableOrView, finds all join tables to this relation.
462502
* For example, if products and categories are linked via product_categories table:
@@ -578,3 +618,47 @@ export type IsStringUnion<T> = string extends T
578618
? false
579619
: true
580620
: false
621+
622+
// Functions matching utils
623+
export type IsMatchingArgs<
624+
FnArgs extends GenericFunction['Args'],
625+
PassedArgs extends GenericFunction['Args']
626+
> = [FnArgs] extends [Record<PropertyKey, never>]
627+
? PassedArgs extends Record<PropertyKey, never>
628+
? true
629+
: false
630+
: keyof PassedArgs extends keyof FnArgs
631+
? PassedArgs extends FnArgs
632+
? true
633+
: false
634+
: false
635+
636+
export type MatchingFunctionArgs<
637+
Fn extends GenericFunction,
638+
Args extends GenericFunction['Args']
639+
> = Fn extends { Args: infer A extends GenericFunction['Args'] }
640+
? IsMatchingArgs<A, Args> extends true
641+
? Fn
642+
: never
643+
: never
644+
645+
export type FindMatchingFunctionByArgs<
646+
FnUnion,
647+
Args extends GenericFunction['Args']
648+
> = FnUnion extends infer Fn extends GenericFunction ? MatchingFunctionArgs<Fn, Args> : never
649+
650+
type MatchingFunctionBySetofFrom<
651+
Fn extends GenericFunction,
652+
TableName extends string
653+
> = Fn['SetofOptions'] extends GenericSetofOption
654+
? TableName extends Fn['SetofOptions']['from']
655+
? Fn
656+
: never
657+
: never
658+
659+
type FindMatchingFunctionBySetofFrom<
660+
FnUnion,
661+
TableName extends string
662+
> = FnUnion extends infer Fn extends GenericFunction
663+
? MatchingFunctionBySetofFrom<Fn, TableName>
664+
: false

src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,17 @@ export type GenericNonUpdatableView = {
6060

6161
export type GenericView = GenericUpdatableView | GenericNonUpdatableView
6262

63+
export type GenericSetofOption = {
64+
isOneToOne?: boolean | undefined
65+
isNotNullable?: boolean | undefined
66+
to: string
67+
from: string
68+
}
69+
6370
export type GenericFunction = {
6471
Args: Record<string, unknown>
6572
Returns: unknown
73+
SetofOptions?: GenericSetofOption
6674
}
6775

6876
export type GenericSchema = {

0 commit comments

Comments
 (0)