Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(types): select query error results and !inner whitespaces #564

Merged
merged 3 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/select-query-parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,14 @@ type ParseField<Input extends string> = Input extends ''
? Name extends 'count'
? ParseCountField<Input>
: Remainder extends `!inner${infer Remainder}`
? ParseEmbeddedResource<Remainder> extends [
? ParseEmbeddedResource<EatWhitespace<Remainder>> extends [
infer Children extends Ast.Node[],
`${infer Remainder}`
]
? // `field!inner(nodes)`
[{ type: 'field'; name: Name; innerJoin: true; children: Children }, Remainder]
: CreateParserErrorIfRequired<
ParseEmbeddedResource<Remainder>,
ParseEmbeddedResource<EatWhitespace<Remainder>>,
`Expected embedded resource after "!inner" at \`${Remainder}\``
>
: EatWhitespace<Remainder> extends `!left${infer Remainder}`
Expand Down
13 changes: 8 additions & 5 deletions src/select-query-parser/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
IsRelationNullable,
ResolveRelationship,
SelectQueryError,
UnwrapErrorMessages,
} from './utils'

/**
Expand All @@ -35,16 +36,18 @@ export type GetResult<
Query extends string
> = Relationships extends null // For .rpc calls the passed relationships will be null in that case, the result will always be the function return type
? ParseQuery<Query> extends infer ParsedQuery extends Ast.Node[]
? RPCCallNodes<ParsedQuery, RelationName extends string ? RelationName : 'rpc_call', Row>
? UnwrapErrorMessages<
RPCCallNodes<ParsedQuery, RelationName extends string ? RelationName : 'rpc_call', Row>
>
: Row
: ParseQuery<Query> extends infer ParsedQuery
? ParsedQuery extends Ast.Node[]
? RelationName extends string
? Relationships extends GenericRelationship[]
? ProcessNodes<Schema, Row, RelationName, Relationships, ParsedQuery>
: SelectQueryError<'Invalid Relationships cannot infer result type'>
: SelectQueryError<'Invalid RelationName cannot infer result type'>
: ParsedQuery
? UnwrapErrorMessages<ProcessNodes<Schema, Row, RelationName, Relationships, ParsedQuery>>
: UnwrapErrorMessages<SelectQueryError<'Invalid Relationships cannot infer result type'>>
: UnwrapErrorMessages<SelectQueryError<'Invalid RelationName cannot infer result type'>>
: UnwrapErrorMessages<ParsedQuery>
: never

/**
Expand Down
10 changes: 10 additions & 0 deletions src/select-query-parser/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ type RequireHintingSelectQueryError<
RelationName extends string
> = SelectQueryError<`Could not embed because more than one relationship was found for '${DistantName}' and '${RelationName}' you need to hint the column with ${DistantName}!<columnName> ?`>

export type UnwrapErrorMessages<T> = T extends SelectQueryError<infer M>
? M
: T extends SelectQueryError<infer M>[]
? M[]
: T extends (infer U)[]
? UnwrapErrorMessages<U>[]
: T extends Record<string, unknown>
? { [K in keyof T]: UnwrapErrorMessages<T[K]> }
: T

export type GetFieldNodeResultName<Field extends Ast.FieldNode> = Field['alias'] extends string
? Field['alias']
: Field['aggregateFunction'] extends AggregateFunctions
Expand Down
50 changes: 50 additions & 0 deletions test/select-query-parser/parser.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,56 @@ import { selectParams } from '../relationships'
])
}

// many-to-one join
{
expectType<ParseQuery<'message, channels (slug)'>>([
{ type: 'field', name: 'message' },
{
type: 'field',
name: 'channels',
children: [{ type: 'field', name: 'slug' }],
},
])
}

// many-to-one join with inner
{
expectType<ParseQuery<'message, channels!inner (slug)'>>([
{ type: 'field', name: 'message' },
{
type: 'field',
name: 'channels',
innerJoin: true,
children: [{ type: 'field', name: 'slug' }],
},
])
}

// many-to-one join with not null
{
expectType<ParseQuery<'message, channels (slug)'>>([
{ type: 'field', name: 'message' },
{
type: 'field',
name: 'channels',
children: [{ type: 'field', name: 'slug' }],
},
])
}

// many-to-one join with inner and not null
{
expectType<ParseQuery<'message, channels!inner (slug)'>>([
{ type: 'field', name: 'message' },
{
type: 'field',
name: 'channels',
innerJoin: true,
children: [{ type: 'field', name: 'slug' }],
},
])
}

// ParserError test cases
// Empty string
{
Expand Down
25 changes: 12 additions & 13 deletions test/select-query-parser/select.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { expectType } from 'tsd'
import { TypeEqual } from 'ts-expect'
import { Json } from '../../src/select-query-parser/types'
import { SelectQueryError } from '../../src/select-query-parser/utils'
import { Prettify } from '../../src/types'
import { Database } from '../types'
import { selectQueries } from '../relationships'
Expand Down Expand Up @@ -198,9 +197,9 @@ type Schema = Database['public']
const { data } = await selectQueries.joinOneToOneWithNullablesNoHint.limit(1).single()
let result: Exclude<typeof data, null>
let expected: {
first_user: SelectQueryError<"Could not embed because more than one relationship was found for 'users' and 'best_friends' you need to hint the column with users!<columnName> ?">
second_user: SelectQueryError<"Could not embed because more than one relationship was found for 'users' and 'best_friends' you need to hint the column with users!<columnName> ?">
third_wheel: SelectQueryError<"Could not embed because more than one relationship was found for 'users' and 'best_friends' you need to hint the column with users!<columnName> ?">
first_user: "Could not embed because more than one relationship was found for 'users' and 'best_friends' you need to hint the column with users!<columnName> ?"
second_user: "Could not embed because more than one relationship was found for 'users' and 'best_friends' you need to hint the column with users!<columnName> ?"
third_wheel: "Could not embed because more than one relationship was found for 'users' and 'best_friends' you need to hint the column with users!<columnName> ?"
}
expectType<TypeEqual<typeof result, typeof expected>>(true)
}
Expand All @@ -222,9 +221,9 @@ type Schema = Database['public']
const { data } = await selectQueries.joinOneToManyWithNullablesNoHint.limit(1).single()
let result: Exclude<typeof data, null>
let expected: {
first_friend_of: SelectQueryError<"Could not embed because more than one relationship was found for 'best_friends' and 'users' you need to hint the column with best_friends!<columnName> ?">
second_friend_of: SelectQueryError<"Could not embed because more than one relationship was found for 'best_friends' and 'users' you need to hint the column with best_friends!<columnName> ?">
third_wheel_of: SelectQueryError<"Could not embed because more than one relationship was found for 'best_friends' and 'users' you need to hint the column with best_friends!<columnName> ?">
first_friend_of: "Could not embed because more than one relationship was found for 'best_friends' and 'users' you need to hint the column with best_friends!<columnName> ?"
second_friend_of: "Could not embed because more than one relationship was found for 'best_friends' and 'users' you need to hint the column with best_friends!<columnName> ?"
third_wheel_of: "Could not embed because more than one relationship was found for 'best_friends' and 'users' you need to hint the column with best_friends!<columnName> ?"
}
expectType<TypeEqual<typeof result, typeof expected>>(true)
}
Expand Down Expand Up @@ -271,7 +270,7 @@ type Schema = Database['public']
id: number
second_user: string
third_wheel: string | null
first_user: SelectQueryError<"Could not embed because more than one relationship was found for 'users' and 'best_friends' you need to hint the column with users!<columnName> ?">
first_user: "Could not embed because more than one relationship was found for 'users' and 'best_friends' you need to hint the column with users!<columnName> ?"
}>
second_friend_of: Array<Database['public']['Tables']['best_friends']['Row']>
third_wheel_of: Array<Database['public']['Tables']['best_friends']['Row']>
Expand Down Expand Up @@ -440,7 +439,7 @@ type Schema = Database['public']
let result: Exclude<typeof data, null>
let expected: {
username: string
messages: SelectQueryError<"column 'sum' does not exist on 'messages'.">[]
messages: "column 'sum' does not exist on 'messages'."[]
}
expectType<TypeEqual<typeof result, typeof expected>>(true)
}
Expand Down Expand Up @@ -630,7 +629,7 @@ type Schema = Database['public']
const { data } = await selectQueries.joinSelectViaColumnHintTwice.limit(1).single()
let result: Exclude<typeof data, null>
let expected: {
users: SelectQueryError<'table "best_friends" specified more than once use hinting for desambiguation'>
users: 'table "best_friends" specified more than once use hinting for desambiguation'
}
expectType<TypeEqual<typeof result, typeof expected>>(true)
}
Expand All @@ -641,7 +640,7 @@ type Schema = Database['public']
let result: Exclude<typeof data, null>
let expected: {
id: number
messages: SelectQueryError<'"channels" and "messages" do not form a many-to-one or one-to-one relationship spread not possible'>
messages: '"channels" and "messages" do not form a many-to-one or one-to-one relationship spread not possible'
}
expectType<TypeEqual<typeof result, typeof expected>>(true)
}
Expand Down Expand Up @@ -684,7 +683,7 @@ type Schema = Database['public']
{
const { data } = await selectQueries.typecastingAndAggregate.limit(1).single()
let result: Exclude<typeof data, null>
let expected: SelectQueryError<`column 'users' does not exist on 'messages'.`>
let expected: `column 'users' does not exist on 'messages'.`
expectType<TypeEqual<typeof result, typeof expected>>(true)
}

Expand Down Expand Up @@ -741,5 +740,5 @@ type Schema = Database['public']
{
const { data, error } = await selectQueries.aggregateOnMissingColumnWithAlias.limit(1).single()
if (error) throw error
expectType<SelectQueryError<`column 'missing_column' does not exist on 'users'.`>>(data)
expectType<`column 'missing_column' does not exist on 'users'.`>(data)
}
Loading