Skip to content

Commit

Permalink
Merge 2b5866f into 0050a1c
Browse files Browse the repository at this point in the history
  • Loading branch information
climba03003 committed Dec 8, 2021
2 parents 0050a1c + 2b5866f commit cabf7a9
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 33 deletions.
82 changes: 73 additions & 9 deletions lib/util/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,80 @@ function shouldRouteHide (schema, opts) {
// This function converts the url in a swagger compliant url string
// => '/user/{id}'
// custom verbs at the end of a url are okay => /user::watch but should be rendered as /user:watch in swagger
function formatParamUrl (url) {
const regex = /(?<!:):([a-zA-Z0-9_]+)/g
let found = regex.exec(url)
while (found !== null) {
const [full, param] = found
url = url.replace(full, '{' + param + '}')
found = regex.exec(url)
const COLON = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
function formatParamUrl (str) {
let i, char
let state = 'skip'
let path = ''
let param = ''
let level = 0
// count for regex if no param exist
let regexp = 0
for (i = 0; i < str.length; i++) {
char = str[i]
switch (state) {
case 'colon': {
// we only accept a-zA-Z0-9_ in param
if (COLON.indexOf(char) !== -1) {
param += char
} else if (char === '(') {
state = 'regexp'
level++
} else {
// end
state = 'skip'
path += '{' + param + '}'
path += char
param = ''
}
break
}
case 'regexp': {
if (char === '(') {
level++
} else if (char === ')') {
level--
}
// we end if the level reach zero
if (level === 0) {
state = 'skip'
if (param === '') {
regexp++
param = 'regexp' + String(regexp)
}
path += '{' + param + '}'
param = ''
}
break
}
default: {
// we check if we need to change state
if (char === ':' && str[i + 1] === ':') {
// double colon -> single colon
path += char
// skip one more
i++
} else if (char === ':') {
// single colon -> state colon
state = 'colon'
} else if (char === '(') {
state = 'regexp'
level++
} else if (char === '*') {
// * -> wildcard
// should be exist once only
path += '{wildcard}'
} else {
path += char
}
}
}
}

return url.replace(/::/g, ':')
// clean up
if (state === 'colon' && param !== '') {
path += '{' + param + '}'
}
return path
}

function resolveLocalRef (jsonSchema, externalSchemas) {
Expand Down
43 changes: 19 additions & 24 deletions test/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,25 @@
const { test } = require('tap')
const { formatParamUrl } = require('../lib/util/common')

test('formatParamUrl', t => {
t.plan(4)

t.test('support /example/:userId', t => {
t.plan(1)
const url = formatParamUrl('/example/:userId')
t.equal(url, '/example/{userId}')
})
const cases = [
['/example/:userId', '/example/{userId}'],
['/example/:userId/:secretToken', '/example/{userId}/{secretToken}'],
['/example/near/:lat-:lng/radius/:r', '/example/near/{lat}-{lng}/radius/{r}'],
['/example/near/:lat_1-:lng_1/radius/:r_1', '/example/near/{lat_1}-{lng_1}/radius/{r_1}'],
['/example/*', '/example/{wildcard}'],
['/example/:file(^\\d+).png', '/example/{file}.png'],
['/example/at/:hour(^\\d{2})h:minute(^\\d{2})m', '/example/at/{hour}h{minute}m'],
['/example/at/(^\\d{2})h(^\\d{2})m', '/example/at/{regexp1}h{regexp2}m'],
['/example/at/(^([0-9]{2})h$)-(^([0-9]{2})m$)', '/example/at/{regexp1}-{regexp2}'],
['/name::verb', '/name:verb'],
['/api/v1/postalcode-jp/:code(^[0-9]{7}$)', '/api/v1/postalcode-jp/{code}'],
['/api/v1/postalcode-jp/(^[0-9]{7}$)', '/api/v1/postalcode-jp/{regexp1}']
]

t.test('support /example/:userId/:secretToken', t => {
t.plan(1)
const url = formatParamUrl('/example/:userId/:secretToken')
t.equal(url, '/example/{userId}/{secretToken}')
})

t.test('support /example/near/:lat-:lng/radius/:r', t => {
t.plan(1)
const url = formatParamUrl('/example/near/:lat-:lng/radius/:r')
t.equal(url, '/example/near/{lat}-{lng}/radius/{r}')
})
test('formatParamUrl', t => {
t.plan(cases.length)

t.test('support /example/near/:lat_1-:lng_1/radius/:r_1', t => {
t.plan(1)
const url = formatParamUrl('/example/near/:lat_1-:lng_1/radius/:r_1')
t.equal(url, '/example/near/{lat_1}-{lng_1}/radius/{r_1}')
})
for (const kase of cases) {
t.equal(formatParamUrl(kase[0]), kase[1])
}
})

0 comments on commit cabf7a9

Please sign in to comment.