@@ -2,7 +2,22 @@ import { ident, literal } from 'pg-format'
22import { DEFAULT_ROLES } from './constants'
33import { rolesSql } from './sql'
44import { PostgresMetaResult , PostgresRole } from './types'
5-
5+ export interface PostgresMetaRoleConfig {
6+ // https://www.rfc-editor.org/rfc/rfc6902
7+ op : 'remove' | 'add' | 'replace'
8+ path : string
9+ value ?: string
10+ }
11+ export function changeRoleConfig2Object ( config : string [ ] ) {
12+ if ( ! config ) {
13+ return null
14+ }
15+ return config . reduce ( ( acc : any , cur ) => {
16+ const [ key , value ] = cur . split ( '=' )
17+ acc [ key ] = value
18+ return acc
19+ } , { } )
20+ }
621export default class PostgresMetaRoles {
722 query : ( sql : string ) => Promise < PostgresMetaResult < any > >
823
3752 if ( offset ) {
3853 sql += ` OFFSET ${ offset } `
3954 }
40- return await this . query ( sql )
55+ const result = await this . query ( sql )
56+ if ( result . data ) {
57+ result . data = result . data . map ( ( role : any ) => {
58+ role . config = changeRoleConfig2Object ( role . config )
59+ return role
60+ } )
61+ }
62+ return result
4163 }
4264
4365 async retrieve ( { id } : { id : number } ) : Promise < PostgresMetaResult < PostgresRole > >
@@ -52,11 +74,13 @@ WHERE
5274 if ( id ) {
5375 const sql = `${ rolesSql } WHERE oid = ${ literal ( id ) } ;`
5476 const { data, error } = await this . query ( sql )
77+
5578 if ( error ) {
5679 return { data, error }
5780 } else if ( data . length === 0 ) {
5881 return { data : null , error : { message : `Cannot find a role with ID ${ id } ` } }
5982 } else {
83+ data [ 0 ] . config = changeRoleConfig2Object ( data [ 0 ] . config )
6084 return { data : data [ 0 ] , error }
6185 }
6286 } else if ( name ) {
6791 } else if ( data . length === 0 ) {
6892 return { data : null , error : { message : `Cannot find a role named ${ name } ` } }
6993 } else {
94+ data [ 0 ] . config = changeRoleConfig2Object ( data [ 0 ] . config )
7095 return { data : data [ 0 ] , error }
7196 }
7297 } else {
89114 member_of,
90115 members,
91116 admins,
117+ config,
92118 } : {
93119 name : string
94120 is_superuser ?: boolean
@@ -104,6 +130,7 @@ WHERE
104130 member_of ?: string [ ]
105131 members ?: string [ ]
106132 admins ?: string [ ]
133+ config ?: Record < string , string >
107134 } ) : Promise < PostgresMetaResult < PostgresRole > > {
108135 const isSuperuserClause = is_superuser ? 'SUPERUSER' : 'NOSUPERUSER'
109136 const canCreateDbClause = can_create_db ? 'CREATEDB' : 'NOCREATEDB'
@@ -118,8 +145,20 @@ WHERE
118145 const memberOfClause = member_of === undefined ? '' : `IN ROLE ${ member_of . join ( ',' ) } `
119146 const membersClause = members === undefined ? '' : `ROLE ${ members . join ( ',' ) } `
120147 const adminsClause = admins === undefined ? '' : `ADMIN ${ admins . join ( ',' ) } `
121-
148+ let configClause = ''
149+ if ( config !== undefined ) {
150+ configClause = Object . keys ( config )
151+ . map ( ( k ) => {
152+ const v = config [ k ]
153+ if ( ! k || ! v ) {
154+ return ''
155+ }
156+ return `ALTER ROLE ${ name } SET ${ k } = ${ v } ;`
157+ } )
158+ . join ( '\n' )
159+ }
122160 const sql = `
161+ BEGIN;
123162CREATE ROLE ${ ident ( name ) }
124163WITH
125164 ${ isSuperuserClause }
134173 ${ validUntilClause }
135174 ${ memberOfClause }
136175 ${ membersClause }
137- ${ adminsClause } ;`
176+ ${ adminsClause } ;
177+ ${ configClause ? configClause : '' }
178+ COMMIT;`
138179 const { error } = await this . query ( sql )
139180 if ( error ) {
140181 return { data : null , error }
156197 connection_limit,
157198 password,
158199 valid_until,
200+ config,
159201 } : {
160202 name ?: string
161203 is_superuser ?: boolean
168210 connection_limit ?: number
169211 password ?: string
170212 valid_until ?: string
213+ config ?: PostgresMetaRoleConfig [ ]
171214 }
172215 ) : Promise < PostgresMetaResult < PostgresRole > > {
173216 const { data : old , error } = await this . retrieve ( { id } )
@@ -209,7 +252,27 @@ WITH
209252 connection_limit === undefined ? '' : `CONNECTION LIMIT ${ connection_limit } `
210253 const passwordClause = password === undefined ? '' : `PASSWORD ${ literal ( password ) } `
211254 const validUntilClause = valid_until === undefined ? '' : `VALID UNTIL ${ literal ( valid_until ) } `
212-
255+ let configClause = ''
256+ if ( config !== undefined ) {
257+ const configSql = config . map ( ( c ) => {
258+ const { op, path, value } = c
259+ const k = path
260+ const v = value || null
261+ if ( ! k ) {
262+ throw new Error ( `Invalid config value ${ value } ` )
263+ }
264+ switch ( op ) {
265+ case 'add' :
266+ case 'replace' :
267+ return `ALTER ROLE ${ ident ( old ! . name ) } SET ${ ident ( k ) } = ${ literal ( v ) } ;`
268+ case 'remove' :
269+ return `ALTER ROLE ${ ident ( old ! . name ) } RESET ${ ident ( k ) } ;`
270+ default :
271+ throw new Error ( `Invalid config op ${ op } ` )
272+ }
273+ } )
274+ configClause = configSql . filter ( Boolean ) . join ( '' )
275+ }
213276 // nameSql must be last
214277 const sql = `
215278BEGIN;
@@ -224,6 +287,7 @@ BEGIN;
224287 ${ connectionLimitClause }
225288 ${ passwordClause }
226289 ${ validUntilClause } ;
290+ ${ configClause ? configClause : '' }
227291 ${ nameSql }
228292COMMIT;`
229293 {
0 commit comments