Skip to content

Commit f12ffd7

Browse files
authored
Merge pull request #118 from i-pip/feat/add_post_delete_on_functions
feat(/functions): adds POST /functions, DELETE /functions and PATCH /functions
2 parents f8757bf + e7a975b commit f12ffd7

File tree

3 files changed

+395
-180
lines changed

3 files changed

+395
-180
lines changed

src/lib/PostgresMetaFunctions.ts

+102-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { literal } from 'pg-format'
1+
import { ident, literal } from 'pg-format'
22
import { DEFAULT_SYSTEM_SCHEMAS } from './constants'
33
import { functionsSql } from './sql'
44
import { PostgresMetaResult, PostgresFunction } from './types'
@@ -22,13 +22,21 @@ export default class PostgresMetaFunctions {
2222
}
2323

2424
async retrieve({ id }: { id: number }): Promise<PostgresMetaResult<PostgresFunction>>
25-
async retrieve({ name }: { name: string }): Promise<PostgresMetaResult<PostgresFunction>>
25+
async retrieve({
26+
name,
27+
schema,
28+
}: {
29+
name: string
30+
schema: string
31+
}): Promise<PostgresMetaResult<PostgresFunction>>
2632
async retrieve({
2733
id,
2834
name,
35+
schema = 'public',
2936
}: {
3037
id?: number
3138
name?: string
39+
schema?: string
3240
}): Promise<PostgresMetaResult<PostgresFunction>> {
3341
if (id) {
3442
const sql = `${functionsSql} WHERE p.oid = ${literal(id)};`
@@ -41,14 +49,16 @@ export default class PostgresMetaFunctions {
4149
return { data: data[0], error }
4250
}
4351
} else if (name) {
44-
const sql = `${functionsSql} WHERE p.proname = ${literal(name)};`
52+
const sql = `${functionsSql} WHERE p.proname = ${literal(name)} AND n.nspname = ${literal(
53+
schema
54+
)};`
4555
const { data, error } = await this.query(sql)
4656
if (error) {
4757
return { data, error }
4858
} else if (data.length === 0) {
4959
return {
5060
data: null,
51-
error: { message: `Cannot find a function named ${name}` },
61+
error: { message: `Cannot find a function named ${name} in schema ${schema}` },
5262
}
5363
} else {
5464
return { data: data[0], error }
@@ -57,4 +67,92 @@ export default class PostgresMetaFunctions {
5767
return { data: null, error: { message: 'Invalid parameters on function retrieve' } }
5868
}
5969
}
70+
71+
async create({
72+
name,
73+
schema = 'public',
74+
params,
75+
definition,
76+
rettype = 'void',
77+
language = 'sql',
78+
}: {
79+
name: string
80+
schema?: string
81+
params?: string[]
82+
definition: string
83+
rettype?: string
84+
language?: string
85+
}): Promise<PostgresMetaResult<PostgresFunction>> {
86+
const sql = `
87+
CREATE FUNCTION ${ident(schema)}.${ident(name)}
88+
${params && params.length ? `(${params.join(',')})` : '()'}
89+
RETURNS ${rettype}
90+
AS ${literal(definition)}
91+
LANGUAGE ${language}
92+
RETURNS NULL ON NULL INPUT;
93+
`
94+
const { error } = await this.query(sql)
95+
if (error) {
96+
return { data: null, error }
97+
}
98+
return await this.retrieve({ name, schema })
99+
}
100+
101+
async update(
102+
id: number,
103+
{
104+
name,
105+
schema,
106+
}: {
107+
name?: string
108+
schema?: string
109+
}
110+
): Promise<PostgresMetaResult<PostgresFunction>> {
111+
const { data: old, error: retrieveError } = await this.retrieve({ id })
112+
if (retrieveError) {
113+
return { data: null, error: retrieveError }
114+
}
115+
116+
const updateNameSql =
117+
name && name !== old!.name
118+
? `ALTER FUNCTION ${ident(old!.schema)}.${ident(old!.name)}(${
119+
old!.argument_types
120+
}) RENAME TO ${ident(name)};`
121+
: ''
122+
123+
const updateSchemaSql =
124+
schema && schema !== old!.schema
125+
? `ALTER FUNCTION ${ident(old!.schema)}.${ident(name || old!.name)}(${
126+
old!.argument_types
127+
}) SET SCHEMA ${ident(schema)};`
128+
: ''
129+
130+
const sql = `BEGIN;${updateNameSql} ${updateSchemaSql} COMMIT;`
131+
132+
const { error } = await this.query(sql)
133+
if (error) {
134+
return { data: null, error }
135+
}
136+
return await this.retrieve({ id })
137+
}
138+
139+
async remove(
140+
id: number,
141+
{ cascade = false } = {}
142+
): Promise<PostgresMetaResult<PostgresFunction>> {
143+
const { data: func, error } = await this.retrieve({ id })
144+
if (error) {
145+
return { data: null, error }
146+
}
147+
const sql = `DROP FUNCTION ${ident(func!.schema)}.${ident(func!.name)}
148+
${func!.argument_types ? `(${func!.argument_types})` : '()'}
149+
${cascade ? 'CASCADE' : 'RESTRICT'};`
150+
{
151+
const { error } = await this.query(sql)
152+
if (error) {
153+
return { data: null, error }
154+
}
155+
}
156+
return { data: func!, error: null }
157+
}
60158
}

src/server/routes/functions.ts

+60
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,64 @@ export default async (fastify: FastifyInstance) => {
4343

4444
return data
4545
})
46+
47+
fastify.post<{
48+
Headers: { pg: string }
49+
Body: any
50+
}>('/', async (request, reply) => {
51+
const connectionString = request.headers.pg
52+
53+
const pgMeta = new PostgresMeta({ connectionString, max: 1 })
54+
const { data, error } = await pgMeta.functions.create(request.body)
55+
await pgMeta.end()
56+
if (error) {
57+
request.log.error(JSON.stringify({ error, req: request.body }))
58+
reply.code(400)
59+
return { error: error.message }
60+
}
61+
return data
62+
})
63+
64+
fastify.patch<{
65+
Headers: { pg: string }
66+
Params: {
67+
id: string
68+
}
69+
Body: any
70+
}>('/:id(\\d+)', async (request, reply) => {
71+
const connectionString = request.headers.pg
72+
const id = Number(request.params.id)
73+
74+
const pgMeta = new PostgresMeta({ connectionString, max: 1 })
75+
const { data, error } = await pgMeta.functions.update(id, request.body)
76+
await pgMeta.end()
77+
if (error) {
78+
request.log.error(JSON.stringify({ error, req: request.body }))
79+
reply.code(400)
80+
if (error.message.startsWith('Cannot find')) reply.code(404)
81+
return { error: error.message }
82+
}
83+
return data
84+
})
85+
86+
fastify.delete<{
87+
Headers: { pg: string }
88+
Params: {
89+
id: string
90+
}
91+
}>('/:id(\\d+)', async (request, reply) => {
92+
const connectionString = request.headers.pg
93+
const id = Number(request.params.id)
94+
95+
const pgMeta = new PostgresMeta({ connectionString, max: 1 })
96+
const { data, error } = await pgMeta.functions.remove(id)
97+
await pgMeta.end()
98+
if (error) {
99+
request.log.error(JSON.stringify({ error, req: request.body }))
100+
reply.code(400)
101+
if (error.message.startsWith('Cannot find')) reply.code(404)
102+
return { error: error.message }
103+
}
104+
return data
105+
})
46106
}

0 commit comments

Comments
 (0)