Skip to content

Commit d2fb929

Browse files
committed
feat: adds /functions
Returns all the database functions and stored procedures
1 parent 4983a25 commit d2fb929

File tree

6 files changed

+108
-0
lines changed

6 files changed

+108
-0
lines changed

src/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ router.use(cors())
77
router.use('/config', addConnectionToRequest, require('./api/config'))
88
router.use('/columns', addConnectionToRequest, require('./api/columns'))
99
router.use('/extensions', addConnectionToRequest, require('./api/extensions'))
10+
router.use('/functions', addConnectionToRequest, require('./api/functions'))
1011
router.use('/query', addConnectionToRequest, require('./api/query'))
1112
router.use('/schemas', addConnectionToRequest, require('./api/schemas'))
1213
router.use('/tables', addConnectionToRequest, require('./api/tables'))

src/api/functions.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Router } from 'express'
2+
3+
import sql = require('../lib/sql')
4+
const { functions } = sql
5+
import { RunQuery } from '../lib/connectionPool'
6+
import { DEFAULT_SYSTEM_SCHEMAS } from '../lib/constants'
7+
import { Functions } from '../lib/interfaces'
8+
9+
const router = Router()
10+
router.get('/', async (req, res) => {
11+
try {
12+
const { data } = await RunQuery(req.headers.pg, functions)
13+
const query: Fetch.QueryParams = req.query
14+
let payload: Functions.Function[] = data
15+
if (!query?.includeSystemSchemas) payload = removeSystemSchemas(data)
16+
return res.status(200).json(payload)
17+
} catch (error) {
18+
console.log('throwing error')
19+
res.status(500).json({ error: 'Database error', status: 500 })
20+
}
21+
})
22+
23+
const removeSystemSchemas = (data: Functions.Function[]) => {
24+
return data.filter((x) => !DEFAULT_SYSTEM_SCHEMAS.includes(x.schema))
25+
}
26+
27+
export = router
28+
29+
/**
30+
* Types
31+
*/
32+
33+
namespace Fetch {
34+
/**
35+
* @param {boolean} [includeSystemSchemas=false] - Return system schemas as well as user schemas
36+
*/
37+
export interface QueryParams {
38+
includeSystemSchemas?: boolean
39+
}
40+
}

src/lib/interfaces.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ export namespace Roles {
3030
}
3131
}
3232

33+
export namespace Functions {
34+
export interface Function {
35+
id: number
36+
schema: string
37+
name: string
38+
language: string
39+
definition: string
40+
argument_types: string
41+
return_type: string
42+
}
43+
}
44+
3345
export namespace Schemas {
3446
export interface Schema {
3547
id: number
@@ -98,6 +110,18 @@ export namespace Tables {
98110
is_updatable: boolean
99111
}
100112

113+
export interface Policy {
114+
id: number
115+
schema: string
116+
table: string
117+
table_id: number
118+
permissive: boolean
119+
roles: string[]
120+
cmd: string
121+
definition: string
122+
with_check: string
123+
}
124+
101125
export interface PrimaryKey {
102126
schema: string
103127
table_name: string

src/lib/sql/functions.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
select
2+
p.oid as id,
3+
n.nspname as schema,
4+
p.proname as name,
5+
l.lanname as language,
6+
case
7+
when l.lanname = 'internal' then p.prosrc
8+
else pg_get_functiondef(p.oid)
9+
end as definition,
10+
pg_get_function_arguments(p.oid) as argument_types,
11+
t.typname as return_type
12+
from
13+
pg_proc p
14+
left join pg_namespace n on p.pronamespace = n.oid
15+
left join pg_language l on p.prolang = l.oid
16+
left join pg_type t on t.oid = p.prorettype

test/integration/index.spec.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,26 @@ describe('/types', () => {
132132
assert.equal(true, !!included)
133133
})
134134
})
135+
describe('/functions', () => {
136+
it('GET', async () => {
137+
const res = await axios.get(`${URL}/functions`)
138+
// console.log('res.data', res.data)
139+
const datum = res.data.find((x) => x.schema == 'public')
140+
const notIncluded = res.data.find((x) => x.schema == 'pg_toast')
141+
assert.equal(res.status, STATUS.SUCCESS)
142+
assert.equal(true, !!datum)
143+
assert.equal(true, !notIncluded)
144+
})
145+
it('GET with system functions', async () => {
146+
const res = await axios.get(`${URL}/functions?includeSystemSchemas=true`)
147+
// console.log('res.data', res.data)
148+
const datum = res.data.find((x) => x.schema == 'public')
149+
const included = res.data.find((x) => x.schema == 'pg_catalog')
150+
assert.equal(res.status, STATUS.SUCCESS)
151+
assert.equal(true, !!datum)
152+
assert.equal(true, !!included)
153+
})
154+
})
135155
describe('/tables', async () => {
136156
it('GET', async () => {
137157
const tables = await axios.get(`${URL}/tables`)

test/postgres/mnt/00-init.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,10 @@ INSERT INTO
2525
VALUES
2626
('Star the repo', 1),
2727
('Watch the releases', 2);
28+
29+
30+
CREATE FUNCTION add(integer, integer) RETURNS integer
31+
AS 'select $1 + $2;'
32+
LANGUAGE SQL
33+
IMMUTABLE
34+
RETURNS NULL ON NULL INPUT;

0 commit comments

Comments
 (0)