-
-
Notifications
You must be signed in to change notification settings - Fork 144
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
Update Type Implementation (postgrest-js side) #152
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,13 @@ | ||
import PostgrestClient from './PostgrestClient' | ||
import PostgrestFilterBuilder from './lib/PostgrestFilterBuilder' | ||
import PostgrestQueryBuilder from './lib/PostgrestQueryBuilder' | ||
import { PostgrestBuilder } from './lib/types' | ||
import { PostgrestBuilder, SchemaBase, TableBase } from './lib/types' | ||
|
||
export { PostgrestClient, PostgrestFilterBuilder, PostgrestQueryBuilder, PostgrestBuilder } | ||
export { | ||
PostgrestClient, | ||
PostgrestFilterBuilder, | ||
PostgrestQueryBuilder, | ||
PostgrestBuilder, | ||
SchemaBase, | ||
TableBase, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
import { PostgrestBuilder } from './types' | ||
import { PostgrestBuilder, TableBase } from './types' | ||
import PostgrestFilterBuilder from './PostgrestFilterBuilder' | ||
import PostgrestTransformBuilder from './PostgrestTransformBuilder' | ||
|
||
export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> { | ||
export default class PostgrestQueryBuilder<T extends TableBase> extends PostgrestBuilder<T> { | ||
constructor( | ||
url: string, | ||
{ headers = {}, schema }: { headers?: { [key: string]: string }; schema?: string } = {} | ||
{ headers = {}, schema }: { headers?: Record<string, string>; schema?: string } = {} | ||
) { | ||
super({} as PostgrestBuilder<T>) | ||
this.url = new URL(url) | ||
|
@@ -21,7 +21,7 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> { | |
* @param count Count algorithm to use to count rows in a table. | ||
*/ | ||
select( | ||
columns = '*', | ||
columns: '*' | string | keyof T | Array<keyof T> = '*', | ||
{ | ||
head = false, | ||
count = null, | ||
|
@@ -31,9 +31,14 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> { | |
} = {} | ||
): PostgrestFilterBuilder<T> { | ||
this.method = 'GET' | ||
|
||
if (Array.isArray(columns)) { | ||
columns = columns.join(',') | ||
} | ||
Comment on lines
+35
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This handles the array select input |
||
|
||
// Remove whitespaces except when quoted | ||
let quoted = false | ||
const cleanedColumns = columns | ||
const cleanedColumns = (columns as string) | ||
.split('') | ||
.map((c) => { | ||
if (/\s/.test(c) && !quoted) { | ||
|
@@ -46,12 +51,15 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> { | |
}) | ||
.join('') | ||
this.url.searchParams.set('select', cleanedColumns) | ||
|
||
if (count) { | ||
this.headers['Prefer'] = `count=${count}` | ||
} | ||
|
||
if (head) { | ||
this.method = 'HEAD' | ||
} | ||
|
||
return new PostgrestFilterBuilder(this) | ||
} | ||
|
||
|
@@ -88,9 +96,9 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> { | |
if (count) { | ||
prefersHeaders.push(`count=${count}`) | ||
} | ||
|
||
this.headers['Prefer'] = prefersHeaders.join(',') | ||
|
||
return new PostgrestFilterBuilder(this) | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,23 +102,75 @@ test('connection error', async () => { | |
expect(isErrorCaught).toBe(true) | ||
}) | ||
|
||
test('custom type', async () => { | ||
interface User { | ||
describe('types', () => { | ||
type User = { | ||
username: string | ||
data: object | null | ||
age_range: string | null | ||
status: 'ONLINE' | 'OFFLINE' | ||
catchphrase: 'string' | null | ||
} | ||
|
||
// TODO: Find a cleaner way to weave a custom type | ||
// eq should show User's properties in LSP/IntelliSense | ||
const { data: users } = <{ data: User[] }>( | ||
await postgrest.from<User>('users').select().eq('username', 'supabot') | ||
) | ||
const user = users[0] | ||
// Autocomplete should show properties of user after '.' | ||
user.username | ||
describe('without schema', () => { | ||
test('everything works without any types passed in', async () => { | ||
// eq should show User's properties in LSP/IntelliSense | ||
const { data: users } = await postgrest.from('users').select().eq('username', 'supabot') | ||
|
||
// Should not error on any property | ||
users[0].username | ||
users[0].somethingElse | ||
Comment on lines
+120
to
+121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are for some reason nullable, but I can't find what in the code actually does it... They weren't nullable before and they aren't in other tests.. |
||
|
||
// Should not error when using properties | ||
const username: string = users[0].username | ||
}) | ||
}) | ||
|
||
type Session = { | ||
id: string | ||
expires: string | ||
} | ||
|
||
type Schema = { | ||
users: User | ||
sessions: Session | ||
} | ||
|
||
describe('with schema', () => { | ||
const typedClient = new PostgrestClient<Schema>(REST_URL) | ||
|
||
test('enforces table and column names', async () => { | ||
// Should error on incorrect table name | ||
// @ts-expect-error | ||
typedClient.from('asdasd') | ||
|
||
// Should error on incorrect column name | ||
// @ts-expect-error | ||
typedClient.from('sessions').select('expires').eq('asdsdasd') | ||
|
||
// Allows array, comma-separated, and * selects | ||
typedClient.from('users').select('username,age_range').eq('username', 'supabot') | ||
typedClient.from('users').select(['username', 'age_range']).eq('username', 'supabot') | ||
typedClient.from('users').select('*').eq('username', 'supabot') | ||
|
||
// Reports incorrect columns in array select | ||
// @ts-expect-error | ||
typedClient.from('users').select(['username', 'age_']).eq('username', 'supabot') | ||
|
||
// eq and select should show User's properties in LSP/IntelliSense | ||
const { data: users } = await typedClient.from('users').select('*').eq('username', 'supabot') | ||
|
||
// Should not error on any property | ||
users[0].username | ||
// Should error on incorrect property | ||
// @ts-expect-error | ||
users[0].somethingElse | ||
|
||
// Returns correct types | ||
const username: string = users[0].username | ||
// @ts-expect-error | ||
const notUsername: number = users[0].catchphrase | ||
}) | ||
}) | ||
}) | ||
|
||
test("don't mutate PostgrestClient.headers", async () => { | ||
|
@@ -170,6 +222,11 @@ test('select with count:exact', async () => { | |
expect(res).toMatchSnapshot() | ||
}) | ||
|
||
test('select with array columns', async () => { | ||
const res = await postgrest.from('users').select(['username', 'age_range'], { count: 'exact' }) | ||
expect(res).toMatchSnapshot() | ||
}) | ||
|
||
test("stored procedure with count: 'exact'", async () => { | ||
const res = await postgrest.rpc('get_status', { name_param: 'supabot', count: 'exact' }) | ||
expect(res).toMatchSnapshot() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will show
'*'
, and the column names as suggestions, while still allowing a comma-separated stringThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is going to be a part of a breaking change, I would recommend removing
string
(and therefore comma separated strings) as an option - IIRC it will then error if you give it an unknown column if a table type is passed in, and will result instring
if no custom table type is passed.