Skip to content

Commit d6a01a9

Browse files
committed
feat: Route Class and add formDataToJson to Parser
1 parent fb36354 commit d6a01a9

File tree

6 files changed

+287
-4
lines changed

6 files changed

+287
-4
lines changed

README.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,32 @@ yarn add @secjs/utils
2525

2626
## Classes Usage
2727

28+
### Route
29+
30+
> Use Route to manipulate paths, getParams, getQueryParams, create route matcher RegExp etc.
31+
32+
```js
33+
import { Route } from '@secjs/utils'
34+
35+
const route = new Route()
36+
const absolutePath = '/tests/:id/users/:user_id'
37+
const path = '/tests/1/users/2?page=1&limit=10'
38+
39+
route.getQueryString(path) // ?page=1&limit=10
40+
route.removeQueryParams(path) // /tests/1/users/2
41+
route.getQueryParamsValue(path) // { page: '1', limit: '10' }
42+
route.getQueryParamsName(path) // ['path', 'limit']
43+
route.getParamsValue(absolutePath, path) // { id: '1', user_id: '10' }
44+
route.getParamsName(absolutePath) // ['id', 'user_id']
45+
46+
const regExpMatcher = route.createMatcher(absolutePath) // /^(?:\/tests\b)(?:\/[\w-]+)(?:\/users\b)(?:\/[\w-]+)$/
47+
48+
regExpMatcher.test(path) // false - because of queryParams
49+
regExpMatcher.test(route.removeQueryParams(path)) // true
50+
```
51+
52+
---
53+
2854
### Blacklist
2955

3056
> Use Blacklist to add, find and remove values from a blacklist/whitelist file
@@ -121,11 +147,15 @@ console.log(parsed2) // 21313
121147

122148
const object = {
123149
joao: 'joao',
124-
lenon: 'lenon',
150+
email: 'lenonsec7@gmail.com',
125151
}
126152
const parsed3 = parser.jsonToFormData(object)
127153

128-
console.log(parsed3) // &joao=joao&lenon=lenon
154+
console.log(parsed3) // &joao=joao&email=lenonSec7%40gmail.com
155+
156+
const parsed4 = parser.formDataToJson('?joao=joao&email=lenonSec7%40gmail.com')
157+
158+
console.log(parsed4) // { joao: 'joao', email: 'lenonsec7@gmail.com' }
129159
```
130160

131161
---

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.2.7",
3+
"version": "1.2.8",
44
"description": "",
55
"scripts": {
66
"build": "tsc",

src/Classes/Parser.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class Parser {
3434
}
3535

3636
/**
37-
* Parse an object form data
37+
* Parse an object to form data
3838
*
3939
* @param object The object to parse
4040
* @return The object parsed to form data
@@ -46,4 +46,24 @@ export class Parser {
4646
}, '')
4747
.substring(1)
4848
}
49+
50+
/**
51+
* Parse form data to json
52+
*
53+
* @param formData The form data to parse
54+
* @return The form data parsed to object
55+
*/
56+
formDataToJson(formData: string): any {
57+
const object = {}
58+
59+
if (formData.startsWith('?')) formData = formData.replace('?', '')
60+
61+
formData.split('&').forEach(queries => {
62+
const query = queries.split('=')
63+
64+
object[decodeURIComponent(query[0])] = decodeURIComponent(query[1])
65+
})
66+
67+
return object
68+
}
4969
}

src/Classes/Route.ts

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { Parser } from './Parser'
2+
3+
export class Route {
4+
/**
5+
* Get the query string in form data format
6+
*
7+
* @param route The route to get the query string
8+
* @return The string with query string
9+
*/
10+
getQueryString(route: string): string {
11+
const queryIndex = route.search(/\?(.*)/)
12+
13+
if (queryIndex === -1) return route
14+
15+
return route.substring(queryIndex)
16+
}
17+
18+
/**
19+
* Remove query params from the route
20+
*
21+
* @param route The route to remove the queryParams
22+
* @return The route without the query params
23+
*/
24+
removeQueryParams(route: string): string {
25+
if (this.getQueryString(route) === route) return route
26+
27+
return route.replace(this.getQueryString(route), '')
28+
}
29+
30+
/**
31+
* Get object with ?&queryParams values from route
32+
*
33+
* @param route The route to get the queryParams
34+
* @return The object of queryParams found inside route
35+
*/
36+
getQueryParamsValue(route: string): any {
37+
return new Parser().formDataToJson(this.getQueryString(route))
38+
}
39+
40+
/**
41+
* Get array with ?&queryParams name from route
42+
*
43+
* @param route The route to get the queryParams
44+
* @return The array name of queryParams found inside route
45+
*/
46+
getQueryParamsName(route: string): string[] {
47+
const queryNames = []
48+
let queryString = this.getQueryString(route)
49+
50+
if (queryString.startsWith('?')) queryString = queryString.replace('?', '')
51+
52+
queryString
53+
.split('&')
54+
.forEach(queries =>
55+
queryNames.push(decodeURIComponent(queries.split('=')[0])),
56+
)
57+
58+
return queryNames
59+
}
60+
61+
/**
62+
* Get object with :params values from route
63+
*
64+
* @param route The route to get the params
65+
* @return The object of params found inside route
66+
*/
67+
getParamsValue(routeWithParams: string, routeWithValues: string): any {
68+
routeWithParams = this.removeQueryParams(routeWithParams)
69+
routeWithValues = this.removeQueryParams(routeWithValues)
70+
71+
const params = {}
72+
73+
const routeWithParamsArray = routeWithParams.split('/')
74+
const routeWithValuesArray = routeWithValues.split('/')
75+
76+
if (routeWithParamsArray.length !== routeWithValuesArray.length) {
77+
throw new Error('DIFFERENT_ROUTES')
78+
}
79+
80+
routeWithParamsArray.forEach((param, i) => {
81+
if (!param.startsWith(':')) return
82+
83+
params[decodeURIComponent(param.replace(':', ''))] = decodeURIComponent(
84+
routeWithValuesArray[i],
85+
)
86+
})
87+
88+
return params
89+
}
90+
91+
/**
92+
* Get array with :params name from route
93+
*
94+
* @param route The route to get the params
95+
* @return The array name of params found inside route
96+
*/
97+
getParamsName(route: string): string[] {
98+
route = this.removeQueryParams(route)
99+
100+
const replaceDots = (value: string): string =>
101+
decodeURIComponent(value.replace(':', ''))
102+
103+
return route.split('/').reduce((results, r) => {
104+
if (r.match(':')) {
105+
results.push(
106+
r.includes('|') ? replaceDots(r.split('|')[0]) : replaceDots(r),
107+
)
108+
}
109+
110+
return results
111+
}, [])
112+
}
113+
114+
/**
115+
* Create a matcher RegExp for any route
116+
*
117+
* @param route The route to create the matcher
118+
* @return The matcher RegExp based on route
119+
*/
120+
createMatcher(route: string): RegExp {
121+
route = this.removeQueryParams(route)
122+
123+
const routeArray = route.split('/')
124+
125+
routeArray.forEach((r, i) => {
126+
if (r === '') return
127+
if (r.startsWith(':')) {
128+
// Match with any value RegExp
129+
routeArray[i] = `(?:\\/[\\w-]+)`
130+
131+
return
132+
}
133+
134+
// Match only with value of ${r} RegExp
135+
routeArray[i] = `(?:\\/${r}\\b)`
136+
})
137+
138+
route = routeArray.join('')
139+
140+
return new RegExp(`^${route}$`)
141+
}
142+
}

tests/Classes/parser.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,15 @@ describe('\n Parser Class', () => {
2929

3030
expect(formData).toBe('name=lenon&email=lenonSec7%40gmail.com')
3131
})
32+
33+
it('should parse form data to json', () => {
34+
const formData = 'name=lenon&email=lenonSec7%40gmail.com'
35+
36+
const json = parser.formDataToJson(formData)
37+
38+
expect(json).toStrictEqual({
39+
name: 'lenon',
40+
email: 'lenonSec7@gmail.com',
41+
})
42+
})
3243
})

tests/Classes/route.spec.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { Token } from './../../src/Classes/Token'
2+
import { Route } from '../../src/Classes/Route'
3+
4+
describe('\n Route Class', () => {
5+
let route: Route
6+
7+
beforeAll(() => {
8+
route = new Route()
9+
})
10+
11+
it('should get query params in string format from the route', () => {
12+
const path = '/users/1/posts?page=1&limit=10&created_at=1995-12-17T03:24:00'
13+
14+
expect(route.getQueryString(path)).toStrictEqual(
15+
'?page=1&limit=10&created_at=1995-12-17T03:24:00',
16+
)
17+
})
18+
19+
it('should remove all query params from the route', () => {
20+
const path = '/users/1/posts?page=1&limit=10&created_at=1995-12-17T03:24:00'
21+
22+
expect(route.removeQueryParams(path)).toStrictEqual('/users/1/posts')
23+
expect(
24+
route.removeQueryParams(route.removeQueryParams(path)),
25+
).toStrictEqual('/users/1/posts')
26+
})
27+
28+
it('should get query params value from any route', () => {
29+
const path = '/users/1/posts?page=1&limit=10&created_at=1995-12-17T03:24:00'
30+
31+
expect(route.getQueryParamsValue(path)).toStrictEqual({
32+
page: '1',
33+
limit: '10',
34+
created_at: '1995-12-17T03:24:00',
35+
})
36+
})
37+
38+
it('should get query params names from any route', () => {
39+
const path = '/users/1/posts?page=1&limit=10&created_at=1995-12-17T03:24:00'
40+
41+
expect(route.getQueryParamsName(path)).toStrictEqual([
42+
'page',
43+
'limit',
44+
'created_at',
45+
])
46+
})
47+
48+
it('should get params value from any route', () => {
49+
const pathWithParams = '/users/:id/posts/:post_id?page=1&limit=10'
50+
const pathWithValues = '/users/1/posts/2?page=1&limit=10'
51+
52+
expect(route.getParamsValue(pathWithParams, pathWithValues)).toStrictEqual({
53+
id: '1',
54+
post_id: '2',
55+
})
56+
})
57+
58+
it('should get params names from any route', () => {
59+
const path = '/users/:id/posts/:post_id?page=1&limit=10'
60+
61+
expect(route.getParamsName(path)).toStrictEqual(['id', 'post_id'])
62+
})
63+
64+
it('should create a matcher RegExp to recognize the route', () => {
65+
const path = '/users/:id/posts/:post_id?page=1&limit=10'
66+
67+
const pathTest1 = '/users/1/posts/test'
68+
const pathTest2 = '/users/1/posts/2/oi'
69+
const pathTest3 = `/users/${new Token().generate()}/posts/1`
70+
71+
const matcher = route.createMatcher(path)
72+
73+
expect(matcher).toStrictEqual(
74+
/^(?:\/users\b)(?:\/[\w-]+)(?:\/posts\b)(?:\/[\w-]+)$/,
75+
)
76+
expect(matcher.test(pathTest1)).toBe(true)
77+
expect(matcher.test(pathTest2)).toBe(false)
78+
expect(matcher.test(pathTest3)).toBe(true)
79+
})
80+
})

0 commit comments

Comments
 (0)