Skip to content

Commit

Permalink
feat: Added getFilters for composite views
Browse files Browse the repository at this point in the history
  • Loading branch information
ardsh committed Aug 23, 2023
1 parent dabf95c commit cf0919f
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilled-peas-perform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'sql-api-engine': patch
---

Added composite views with getFilters
30 changes: 23 additions & 7 deletions packages/core/engine/buildView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
ValueExpression,
SqlFragment,
QuerySqlToken,
CommonQueryMethods
} from 'slonik'
import { z } from 'zod'
import {
Expand Down Expand Up @@ -75,11 +74,7 @@ export type Interpretors<

type BuildView<
TFilter extends Record<string, any> = Record<never, any>,
TFilterKey extends keyof TFilter = keyof TFilter extends Record<infer K, any>
? K extends string
? K
: never
: never
TFilterKey extends keyof TFilter = never
> = {
/**
* Allows adding custom filters to the view
Expand All @@ -99,7 +94,7 @@ type BuildView<
>(filters: {
[x in TNewFilterKey]?: (
filter: TNewFilter[x],
allFilters: TFilter,
allFilters: TFilter & TNewFilter,
context: any
) =>
| Promise<SqlFragment | null | undefined | false>
Expand Down Expand Up @@ -153,6 +148,19 @@ type BuildView<
[x in TFilterKey]?: TFilter[x]
}>
}): Promise<QuerySqlToken>
getFilters<TPrefix extends string>(prefix: TPrefix): {
[x in TFilterKey extends `${TPrefix}${string}` ? TFilterKey : `${TPrefix}${Extract<TFilterKey, string>}`]?:
(
filter: TFilter[x extends `${TPrefix}${infer K}` ? K extends TFilterKey ? K : x : x],
allFilters: any,
context: any
) =>
| Promise<SqlFragment | null | undefined | false>
| SqlFragment
| null
| undefined
| false
}
} & SqlFragment

export const buildView = (
Expand All @@ -162,6 +170,7 @@ export const buildView = (
if (!parts[0]?.match(/^\s*FROM/i)) {
throw new Error('First part of view must be FROM')
}
const table = parts[0].match(/^\s*FROM\s+(\S+)/i)?.[1];
const fromFragment = sql.fragment(parts, ...values)
const interpreters = {} as Interpretors<Record<string, any>>

Expand All @@ -181,6 +190,13 @@ export const buildView = (
Object.assign(interpreters, filters)
return self
},
getFilters(prefix: string) {
const filters = {} as any
for (const key of Object.keys(interpreters)) {
filters[prefix + key.replace(prefix, '')] = (interpreters as any)[key];
}
return filters
},
addStringFilter: (key: string, mapper?: any) => {
return self.addFilters({
[key]: (...args: any) =>
Expand Down
6 changes: 4 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"lint": "eslint \"./**/*.ts*\"",
"type-check": "tsc --noEmit",
"prettier-check": "prettier --check \"./**/*.ts*\"",
"test": "jest --forceExit --env @edge-runtime/jest-environment .test.ts && jest --forceExit --env node .test.ts"
"tdd": "jest --watch --forceExit --env node .test.ts",
"test": "jest --forceExit --env node .test.ts || jest --forceExit --env @edge-runtime/jest-environment .test.ts"
},
"exports": {
"./package.json": "./package.json",
Expand Down Expand Up @@ -56,11 +57,12 @@
"slonik": "^34.1.0"
},
"devDependencies": {
"@edge-runtime/jest-environment": "^2.3.0",
"@sql-api/tsconfig": "workspace:*",
"@types/jest": "^29.2.0",
"@types/node": "^17.0.12",
"@types/react": "^18.2.8",
"@types/react-dom": "^18.2.0",
"@sql-api/tsconfig": "workspace:*",
"eslint": "^8.47.0",
"eslint-config-shared": "workspace:*",
"jest": "^29.6.2",
Expand Down
18 changes: 14 additions & 4 deletions packages/core/tests/buildView.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,27 @@ import { buildView } from '../engine/buildView'
const { db } = makeQueryTester('buildView')

it('Allows building a view from a string', async () => {
const view = buildView`FROM users`
const usersView = buildView`FROM users`
.addFilters({
test: (value: string) => sql.fragment`email = ${value}`,
name: (value: string) => sql.fragment`first_name = ${value}`
})
.addStringFilter('last_name')
.addStringFilter('users.last_name')
.addBooleanFilter('long_email', () => sql.fragment`LENGTH(email) > 10`)

const compositeView = buildView`FROM users
LEFT JOIN test_table_bar ON test_table_bar.uid = users.id`
.addFilters(usersView.getFilters('users.'))
expect(
(await compositeView.getQuery({
where: {
"users.long_email": true,
}
})).sql).toMatch("LENGTH(email) > 10");
const data = await db.any(
await view.getQuery({
await usersView.getQuery({
where: {
last_name: {
"users.last_name": {
_ilike: '%e%'
},
long_email: true,
Expand Down
71 changes: 70 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit cf0919f

Please sign in to comment.