Skip to content

Commit

Permalink
feat: Adding head and count option on select(), insert(), update(), d…
Browse files Browse the repository at this point in the history
…elete(), and rpc() (#147)

* select method now accepts home and count option

* removed head and count option from PostgrestTransformBuilder and added home and count option to select in PostgrestQueryBuilder

* select now returns count value when count option is selected

* Enabled head for select

* Updated the test snapshots to include count field

* Set default values for head and count

Co-authored-by: Bobbie Soedirgo <bobbie@soedirgo.dev>

* Added basic test cases for select with head and count parameters

* Added count and head option to rpc, and added count option to insert, update, delete

* Added count tests for insert, update, delete rpc

* Added head and count test for rpc

* Updated test so that count value for 'planned' and 'estimated' would match any number

Co-authored-by: Bobbie Soedirgo <bobbie@soedirgo.dev>
  • Loading branch information
dshukertjr and soedirgo committed Jan 15, 2021
1 parent d56d69a commit 8cf6e00
Show file tree
Hide file tree
Showing 7 changed files with 986 additions and 20 deletions.
1 change: 0 additions & 1 deletion src/PostgrestClient.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import PostgrestQueryBuilder from './lib/PostgrestQueryBuilder'
import PostgrestTransformBuilder from './lib/PostgrestTransformBuilder'
import { PostgrestBuilder } from './lib/types'

export default class PostgrestClient {
url: string
Expand Down
72 changes: 66 additions & 6 deletions src/lib/PostgrestQueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,19 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
* Performs vertical filtering with SELECT.
*
* @param columns The columns to retrieve, separated by commas.
* @param head When set to true, select will void data.
* @param count Count algorithm to use to count rows in a table.
*/
select(columns = '*'): PostgrestFilterBuilder<T> {
select(
columns = '*',
{
head = false,
count = null,
}: {
head?: boolean
count?: null | 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestFilterBuilder<T> {
this.method = 'GET'
// Remove whitespaces except when quoted
let quoted = false
Expand All @@ -35,6 +46,12 @@ 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)
}

Expand All @@ -52,10 +69,12 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
upsert = false,
onConflict,
returning = 'representation',
count = null,
}: {
upsert?: boolean
onConflict?: string
returning?: 'minimal' | 'representation'
count?: null | 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestFilterBuilder<T> {
this.method = 'POST'
Expand All @@ -67,6 +86,10 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {

if (upsert && onConflict !== undefined) this.url.searchParams.set('on_conflict', onConflict)
this.body = values
if (count) {
prefersHeaders.push(`count=${count}`)
this.headers['Prefer'] = prefersHeaders.join(',')
}
return new PostgrestFilterBuilder(this)
}

Expand All @@ -78,11 +101,23 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
*/
update(
values: Partial<T>,
{ returning = 'representation' }: { returning?: 'minimal' | 'representation' } = {}
{
returning = 'representation',
count = null,
}: {
returning?: 'minimal' | 'representation'
count?: null | 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestFilterBuilder<T> {
this.method = 'PATCH'
this.headers['Prefer'] = `return=${returning}`
let prefersHeaders = []
prefersHeaders.push(`return=${returning}`)
this.headers['Prefer'] = prefersHeaders.join(',')
this.body = values
if (count) {
prefersHeaders.push(`count=${count}`)
this.headers['Prefer'] = prefersHeaders.join(',')
}
return new PostgrestFilterBuilder(this)
}

Expand All @@ -93,16 +128,41 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
*/
delete({
returning = 'representation',
}: { returning?: 'minimal' | 'representation' } = {}): PostgrestFilterBuilder<T> {
count = null,
}: {
returning?: 'minimal' | 'representation'
count?: null | 'exact' | 'planned' | 'estimated'
} = {}): PostgrestFilterBuilder<T> {
this.method = 'DELETE'
this.headers['Prefer'] = `return=${returning}`
let prefersHeaders = []
prefersHeaders.push(`return=${returning}`)
this.headers['Prefer'] = prefersHeaders.join(',')
if (count) {
prefersHeaders.push(`count=${count}`)
this.headers['Prefer'] = prefersHeaders.join(',')
}
return new PostgrestFilterBuilder(this)
}

/** @internal */
rpc(params?: object): PostgrestTransformBuilder<T> {
rpc(
params?: object,
{
head = false,
count = null,
}: {
head?: boolean
count?: null | 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestTransformBuilder<T> {
this.method = 'POST'
this.body = params
if (count) {
this.headers['Prefer'] = `count=${count}`
}
if (head) {
this.method = 'HEAD'
}
return new PostgrestTransformBuilder(this)
}
}
11 changes: 2 additions & 9 deletions src/lib/PostgrestTransformBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,7 @@ export default class PostgrestTransformBuilder<T> extends PostgrestBuilder<T> {
* @param count The maximum no. of rows to limit to.
* @param foreignTable The foreign table to use (for foreign columns).
*/
limit(
count: number,
{ foreignTable }: { foreignTable?: string } = {}
): this {
limit(count: number, { foreignTable }: { foreignTable?: string } = {}): this {
const key = typeof foreignTable === 'undefined' ? 'limit' : `"${foreignTable}".limit`
this.url.searchParams.set(key, `${count}`)
return this
Expand All @@ -75,11 +72,7 @@ export default class PostgrestTransformBuilder<T> extends PostgrestBuilder<T> {
* @param to The last index to which to limit the result, inclusive.
* @param foreignTable The foreign table to use (for foreign columns).
*/
range(
from: number,
to: number,
{ foreignTable }: { foreignTable?: string } = {}
): this {
range(from: number, to: number, { foreignTable }: { foreignTable?: string } = {}): this {
const keyOffset = typeof foreignTable === 'undefined' ? 'offset' : `"${foreignTable}".offset`
const keyLimit = typeof foreignTable === 'undefined' ? 'limit' : `"${foreignTable}".limit`
this.url.searchParams.set(keyOffset, `${from}`)
Expand Down
26 changes: 23 additions & 3 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ interface PostgrestResponseSuccess<T> extends PostgrestResponseBase {
error: null
data: T[]
body: T[]
count: number | null
}
interface PostgrestResponseFailure extends PostgrestResponseBase {
error: PostgrestError
data: null
// For backward compatibility: body === data
body: null
count: null
}
export type PostgrestResponse<T> = PostgrestResponseSuccess<T> | PostgrestResponseFailure

Expand Down Expand Up @@ -80,18 +82,36 @@ export abstract class PostgrestBuilder<T> implements PromiseLike<PostgrestRespon
body: JSON.stringify(this.body),
})
.then(async (res) => {
let error, data
let error, data, count
if (res.ok) {
error = null
const isReturnMinimal = this.headers['Prefer']?.split(',').includes('return=minimal')
data = isReturnMinimal ? null : await res.json()
if (this.method !== 'HEAD') {
const isReturnMinimal = this.headers['Prefer']?.split(',').includes('return=minimal')
data = isReturnMinimal ? null : await res.json()
} else {
data = null
}

const countHeader = this.headers['Prefer']?.match(/count=(exact|planned|estimated)/)
if (countHeader) {
const contentRange = res.headers.get('content-range')?.split('/')
if (contentRange && contentRange.length > 1) {
count = parseInt(contentRange[1])
} else {
count = null
}
} else {
count = null
}
} else {
error = await res.json()
data = null
count = null
}
const postgrestResponse: PostgrestResponse<T> = {
error,
data,
count,
status: res.status,
statusText: res.statusText,
body: data,
Expand Down

0 comments on commit 8cf6e00

Please sign in to comment.