Skip to content

Commit 33c81f6

Browse files
committed
feat: add database url parser to Parser class
1 parent af6f290 commit 33c81f6

File tree

7 files changed

+254
-8
lines changed

7 files changed

+254
-8
lines changed

README.md

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,37 @@ Parser.reasonToStatusCode('NOT_found') // 404
586586
Parser.reasonToStatusCode('internal server error') // 500
587587
```
588588

589+
```ts
590+
const url =
591+
'postgresql://postgres:root@127.0.0.1:5432/postgres?paramOne=1&paramTwo=2&paramThree=3'
592+
593+
// Convert database connection url to connection object
594+
const connectionObject = Parser.dbUrlToConnectionObj(url)
595+
596+
/** connectionObject result
597+
* {
598+
* protocol: 'postgresql',
599+
* user: 'postgres',
600+
* password: 'root',
601+
* host: '127.0.0.1',
602+
* port: 5432,
603+
* database: 'postgres',
604+
* options: {
605+
* paramOne: '1',
606+
* paramTwo: '2',
607+
* paramThree: '3',
608+
* }
609+
* }
610+
*/
611+
612+
// Convert connection object to database connection url
613+
const connectionUrl = Parser.connectionObjToDbUrl(connectionObject)
614+
615+
/** connectionUrl result
616+
* postgresql://postgres:root@127.0.0.1:5432/postgres?paramOne=1&paramTwo=2&paramThree=3
617+
*/
618+
```
619+
589620
---
590621

591622
### Clean
@@ -765,18 +796,18 @@ await sleep(2000) // Your code will stop in this line for two seconds
765796

766797
> Find out what's the distance between a coordinate to other
767798
768-
```js
799+
```ts
769800
import { kmRadius, ICoordinate } from '@secjs/utils'
770801

771802
// Use type number for more precision,
772803
// but you can use string to,
773804
// kmRadius will handle it with Parser.
774-
const coordinate1 {
805+
const coordinate1 = {
775806
latitude: -25.4858841,
776807
longitude: -54.564615,
777808
} as ICoordinate // ICoordinate will force numbers
778809

779-
const coordinate2 {
810+
const coordinate2 = {
780811
latitude: '-54.564615',
781812
longitude: '-25.4858841',
782813
}

index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export * from './src/Contracts/DBUrlParserContract'
2+
13
export * from './src/Classes/Is'
24
export * from './src/Classes/Json'
35
export * from './src/Classes/Path'

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@secjs/utils",
3-
"version": "1.6.9",
3+
"version": "1.7.0",
44
"description": "",
55
"license": "MIT",
66
"author": "João Lenon",

src/Classes/Parser.ts

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
import ms from 'ms'
1111
import bytes from 'bytes'
1212

13-
import { getReasonPhrase, getStatusCode } from 'http-status-codes'
14-
import { InternalServerException } from '@secjs/exceptions'
13+
import { Is } from './Is'
1514
import { String } from './String'
15+
import { InternalServerException } from '@secjs/exceptions'
16+
import { getReasonPhrase, getStatusCode } from 'http-status-codes'
17+
import { DBUrlParserContract } from '../Contracts/DBUrlParserContract'
18+
import { homedir } from 'os'
1619

1720
export class Parser {
1821
/**
@@ -199,4 +202,83 @@ export class Parser {
199202

200203
return getStatusCode(reason)
201204
}
205+
206+
/**
207+
* dbUrlToConnectionObj parses the database connection url to connection object
208+
*
209+
* @param url - The database url
210+
* @return Return the connection object
211+
*/
212+
static dbUrlToConnectionObj(url: string): DBUrlParserContract {
213+
const urlRegexp = /^([^:\\/\s]+):\/\/((.*):(.*)@|)(.*)(:(.*)|)\/(.*)(\?(.+))?/
214+
const matcher = url.match(urlRegexp)
215+
216+
const connectionObject: DBUrlParserContract = {
217+
protocol: matcher[1],
218+
user: null,
219+
password: null,
220+
host: null,
221+
port: null,
222+
database: matcher[8],
223+
options: {},
224+
}
225+
226+
if (matcher[5].includes(',')) {
227+
connectionObject.host = matcher[5].split(',')
228+
} else {
229+
connectionObject.host = matcher[5]
230+
231+
if (matcher[5].includes(':')) {
232+
const [h, p] = matcher[5].split(':')
233+
234+
connectionObject.host = h
235+
connectionObject.port = parseInt(p)
236+
}
237+
}
238+
239+
if (connectionObject.database.includes('?')) {
240+
const [database, options] = connectionObject.database.split('?')
241+
242+
connectionObject.database = database
243+
connectionObject.options = this.formDataToJson(options)
244+
}
245+
246+
if (matcher[3]) connectionObject.user = matcher[3]
247+
if (matcher[4]) connectionObject.password = matcher[4]
248+
249+
return connectionObject
250+
}
251+
252+
/**
253+
* connectionObjToDbUrl parses the database connection object to connection url
254+
*
255+
* @param object - The database connection object
256+
* @return Return the connection url
257+
*/
258+
static connectionObjToDbUrl(object: DBUrlParserContract): string {
259+
const { protocol, user, password, host, port, database, options } = object
260+
261+
let url = `${protocol}://`
262+
263+
if (user || password) {
264+
if (user) url = url.concat(user)
265+
if (password) url = url.concat(`:${password}`)
266+
267+
url = url.concat('@')
268+
}
269+
270+
if (Is.Array(host)) {
271+
url = url.concat(host.join(','))
272+
} else {
273+
url = url.concat(host)
274+
275+
if (port) url = url.concat(`:${port}`)
276+
}
277+
278+
url = url.concat(`/${database}`)
279+
280+
if (!Is.Empty(options)) url = url.concat(`?${this.jsonToFormData(options)}`)
281+
282+
return url
283+
}
202284
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @secjs/utils
3+
*
4+
* (c) João Lenon <lenon@secjs.com.br>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
export interface DBUrlParserContract {
11+
protocol: string
12+
user?: string
13+
password?: string
14+
host: string | string[]
15+
port?: number
16+
database: string
17+
options?: Record<string, string>
18+
}

tests/Classes/parser.spec.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,117 @@ describe('\n Parser Class', () => {
105105
expect(Parser.reasonToStatusCode('unauthorized')).toBe(401)
106106
expect(Parser.reasonToStatusCode('INTERNAL_SERVER_ERROR')).toBe(500)
107107
})
108+
109+
it('should parse the complete database url to object and object to complete database url', async () => {
110+
const url =
111+
'postgresql://postgres:root@127.0.0.1:5432/postgres?paramOne=1&paramTwo=2&paramThree=3'
112+
113+
// database url to connection object
114+
const connectionObject = Parser.dbUrlToConnectionObj(url)
115+
116+
expect(connectionObject.protocol).toBe('postgresql')
117+
expect(connectionObject.user).toBe('postgres')
118+
expect(connectionObject.password).toBe('root')
119+
expect(connectionObject.host).toBe('127.0.0.1')
120+
expect(connectionObject.port).toBe(5432)
121+
expect(connectionObject.database).toBe('postgres')
122+
expect(connectionObject.options).toEqual({
123+
paramOne: '1',
124+
paramTwo: '2',
125+
paramThree: '3',
126+
})
127+
128+
// connection object to database url
129+
const connectionUrl = Parser.connectionObjToDbUrl(connectionObject)
130+
131+
expect(connectionUrl).toBe(url)
132+
})
133+
134+
it('should parse the without auth database url to object and object to without auth database url', async () => {
135+
const url = 'postgresql://127.0.0.1:5432/postgres'
136+
137+
// database url to connection object
138+
const connectionObject = Parser.dbUrlToConnectionObj(url)
139+
140+
expect(connectionObject.protocol).toBe('postgresql')
141+
expect(connectionObject.user).toBe(null)
142+
expect(connectionObject.password).toBe(null)
143+
expect(connectionObject.host).toBe('127.0.0.1')
144+
expect(connectionObject.port).toBe(5432)
145+
expect(connectionObject.database).toBe('postgres')
146+
expect(connectionObject.options).toEqual({})
147+
148+
// connection object to database url
149+
const connectionUrl = Parser.connectionObjToDbUrl(connectionObject)
150+
151+
expect(connectionUrl).toBe(url)
152+
})
153+
154+
it('should parse the without auth and port database url to object and object to without auth and port database url', async () => {
155+
const url = 'postgresql://127.0.0.1/postgres?options=10&test=10'
156+
157+
// database url to connection object
158+
const connectionObject = Parser.dbUrlToConnectionObj(url)
159+
160+
expect(connectionObject.protocol).toBe('postgresql')
161+
expect(connectionObject.user).toBe(null)
162+
expect(connectionObject.password).toBe(null)
163+
expect(connectionObject.host).toBe('127.0.0.1')
164+
expect(connectionObject.port).toBe(null)
165+
expect(connectionObject.database).toBe('postgres')
166+
expect(connectionObject.options).toEqual({
167+
options: '10',
168+
test: '10',
169+
})
170+
171+
// connection object to database url
172+
const connectionUrl = Parser.connectionObjToDbUrl(connectionObject)
173+
174+
expect(connectionUrl).toBe(url)
175+
})
176+
177+
it('should parse the without auth, port and options database url to object and object to without auth, port and options database url', async () => {
178+
const url = 'postgresql://127.0.0.1/postgres'
179+
180+
// database url to connection object
181+
const connectionObject = Parser.dbUrlToConnectionObj(url)
182+
183+
expect(connectionObject.protocol).toBe('postgresql')
184+
expect(connectionObject.user).toBe(null)
185+
expect(connectionObject.password).toBe(null)
186+
expect(connectionObject.host).toBe('127.0.0.1')
187+
expect(connectionObject.port).toBe(null)
188+
expect(connectionObject.database).toBe('postgres')
189+
expect(connectionObject.options).toEqual({})
190+
191+
// connection object to database url
192+
const connectionUrl = Parser.connectionObjToDbUrl(connectionObject)
193+
194+
expect(connectionUrl).toBe(url)
195+
})
196+
197+
it('should parse the cluster database url to object and object to cluster database url', async () => {
198+
const url =
199+
'postgresql://postgres:root@127.0.0.1:5432,127.0.0.1:5433,127.0.0.1:5434/postgres'
200+
201+
// database url to connection object
202+
const connectionObject = Parser.dbUrlToConnectionObj(url)
203+
204+
expect(connectionObject.protocol).toBe('postgresql')
205+
expect(connectionObject.user).toBe('postgres')
206+
expect(connectionObject.password).toBe('root')
207+
expect(connectionObject.host).toEqual([
208+
'127.0.0.1:5432',
209+
'127.0.0.1:5433',
210+
'127.0.0.1:5434',
211+
])
212+
expect(connectionObject.port).toBe(null)
213+
expect(connectionObject.database).toBe('postgres')
214+
expect(connectionObject.options).toEqual({})
215+
216+
// connection object to database url
217+
const connectionUrl = Parser.connectionObjToDbUrl(connectionObject)
218+
219+
expect(connectionUrl).toBe(url)
220+
})
108221
})

0 commit comments

Comments
 (0)