Skip to content

Commit 18a156f

Browse files
authored
Merge branch 'develop' into feature/modify_tables
2 parents 5de5901 + d2fb929 commit 18a156f

File tree

11 files changed

+139
-29
lines changed

11 files changed

+139
-29
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/api/tables.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Router } from 'express'
22
import SQL from 'sql-template-strings'
33
import sqlTemplates = require('../lib/sql')
4-
const { columns, grants, primary_keys, relationships, tables } = sqlTemplates
4+
const { columns, grants, policies, primary_keys, relationships, tables } = sqlTemplates
55
import { coalesceRowsToArray } from '../lib/helpers'
66
import { RunQuery } from '../lib/connectionPool'
77
import { DEFAULT_SYSTEM_SCHEMAS } from '../lib/constants'
@@ -15,12 +15,14 @@ router.get('/', async (req, res) => {
1515
WITH tables AS ( ${tables} ),
1616
columns AS ( ${columns} ),
1717
grants AS ( ${grants} ),
18+
policies AS ( ${policies} ),
1819
primary_keys AS ( ${primary_keys} ),
1920
relationships AS ( ${relationships} )
2021
SELECT
2122
*,
2223
${coalesceRowsToArray('columns', 'SELECT * FROM columns WHERE columns.table_id = tables.id')},
2324
${coalesceRowsToArray('grants', 'SELECT * FROM grants WHERE grants.table_id = tables.id')},
25+
${coalesceRowsToArray('policies', 'SELECT * FROM policies WHERE policies.table_id = tables.id')},
2426
${coalesceRowsToArray(
2527
'primary_keys',
2628
'SELECT * FROM primary_keys WHERE primary_keys.table_id = tables.id'

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/extensions_enabled.sql

Lines changed: 0 additions & 13 deletions
This file was deleted.

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

src/lib/sql/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export = {
66
config: fs.readFileSync(path.join(__dirname, '/config.sql')).toString(),
77
constraints: fs.readFileSync(path.join(__dirname, '/constraints.sql')).toString(),
88
extensions: fs.readFileSync(path.join(__dirname, '/extensions.sql')).toString(),
9+
functions: fs.readFileSync(path.join(__dirname, '/functions.sql')).toString(),
910
grants: fs.readFileSync(path.join(__dirname, '/grants.sql')).toString(),
1011
joins: fs.readFileSync(path.join(__dirname, '/joins.sql')).toString(),
1112
primary_keys: fs.readFileSync(path.join(__dirname, '/primary_keys.sql')).toString(),

src/lib/sql/policies.sql

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
SELECT
2-
n.nspname AS schemaname,
3-
c.relname AS tablename,
4-
pol.polname AS policyname,
1+
select
2+
n.oid as id,
3+
n.nspname AS schema,
4+
c.relname AS table,
5+
c.oid AS table_id,
6+
pol.polname AS name,
57
CASE
68
WHEN pol.polpermissive THEN 'PERMISSIVE' :: text
79
ELSE 'RESTRICTIVE' :: text
810
END AS permissive,
911
CASE
10-
WHEN pol.polroles = '{0}' :: oid [] THEN string_to_array('public' :: text, '' :: text) :: name []
11-
ELSE ARRAY(
12+
WHEN pol.polroles = '{0}' :: oid []
13+
THEN array_to_json(string_to_array('public' :: text, '' :: text) :: name [])
14+
ELSE array_to_json(ARRAY(
1215
SELECT
1316
pg_authid.rolname
1417
FROM
@@ -17,7 +20,7 @@ SELECT
1720
pg_authid.oid = ANY (pol.polroles)
1821
ORDER BY
1922
pg_authid.rolname
20-
)
23+
))
2124
END AS roles,
2225
CASE
2326
pol.polcmd
@@ -33,4 +36,4 @@ SELECT
3336
FROM
3437
pg_policy pol
3538
JOIN pg_class c ON c.oid = pol.polrelid
36-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
39+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace

test/integration/index.spec.js

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,16 +141,36 @@ describe('/types', () => {
141141
assert.equal(true, !!included)
142142
})
143143
})
144-
describe('/tables & /columns', async () => {
145-
it('GET /tables', async () => {
144+
describe('/functions', () => {
145+
it('GET', async () => {
146+
const res = await axios.get(`${URL}/functions`)
147+
// console.log('res.data', res.data)
148+
const datum = res.data.find((x) => x.schema == 'public')
149+
const notIncluded = res.data.find((x) => x.schema == 'pg_toast')
150+
assert.equal(res.status, STATUS.SUCCESS)
151+
assert.equal(true, !!datum)
152+
assert.equal(true, !notIncluded)
153+
})
154+
it('GET with system functions', async () => {
155+
const res = await axios.get(`${URL}/functions?includeSystemSchemas=true`)
156+
// console.log('res.data', res.data)
157+
const datum = res.data.find((x) => x.schema == 'public')
158+
const included = res.data.find((x) => x.schema == 'pg_catalog')
159+
assert.equal(res.status, STATUS.SUCCESS)
160+
assert.equal(true, !!datum)
161+
assert.equal(true, !!included)
162+
})
163+
})
164+
describe('/tables', async () => {
165+
it('GET', async () => {
146166
const tables = await axios.get(`${URL}/tables`)
147167
const datum = tables.data.find((x) => `${x.schema}.${x.name}` === 'public.users')
148168
const notIncluded = tables.data.find((x) => `${x.schema}.${x.name}` === 'pg_catalog.pg_type')
149169
assert.equal(tables.status, STATUS.SUCCESS)
150170
assert.equal(true, !!datum)
151171
assert.equal(true, !notIncluded)
152172
})
153-
it('/tables should return the columns', async () => {
173+
it('should return the columns, grants, and policies', async () => {
154174
const tables = await axios.get(`${URL}/tables`)
155175
const datum = tables.data.find((x) => `${x.schema}.${x.name}` === 'public.users')
156176
const idColumn = datum.columns.find((x) => x.name === 'id')
@@ -161,11 +181,8 @@ describe('/tables & /columns', async () => {
161181
assert.equal(idColumn.is_updatable, true)
162182
assert.equal(idColumn.is_identity, true)
163183
assert.equal(nameColumn.is_identity, false)
164-
})
165-
it('/tables should return the grants', async () => {
166-
const tables = await axios.get(`${URL}/tables`)
167-
const datum = tables.data.find((x) => `${x.schema}.${x.name}` === 'public.users')
168184
assert.equal(datum.grants.length > 0, true)
185+
assert.equal(datum.policies.length == 0, true)
169186
})
170187
it('/tables should return the relationships', async () => {
171188
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;

test/postgres/mnt/01-memes.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
1+
2+
13
CREATE TABLE public.category (
24
id serial NOT NULL PRIMARY KEY,
35
name text NOT NULL
46
);
57

8+
-- Fake policies
9+
grant select, update(name)
10+
on category
11+
to postgres;
12+
13+
create policy categories_update_policy
14+
on category for update
15+
to postgres
16+
using(current_setting('my.username') IN (name));
17+
618
INSERT INTO public.category (id, name) VALUES
719
(1, 'Funny'),
820
(2, 'Weird'),

0 commit comments

Comments
 (0)