Add `redirect` macro to allow path redirection.
Add tests for `redirect` macro. Add check for unique patterns in path and tests.
This commit is contained in:
parent
04c2258721
commit
991b5836f6
|
@ -45,6 +45,18 @@ proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val =
|
||||||
for j in 0..<x.entries:
|
for j in 0..<x.entries:
|
||||||
if eq(key, x.keys[j]): return x.vals[j]
|
if eq(key, x.keys[j]): return x.vals[j]
|
||||||
|
|
||||||
|
proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key, default: Val): Val =
|
||||||
|
var x = b.root
|
||||||
|
while x.isInternal:
|
||||||
|
for j in 0..<x.entries:
|
||||||
|
if j+1 == x.entries or less(key, x.keys[j+1]):
|
||||||
|
x = x.links[j]
|
||||||
|
break
|
||||||
|
assert(not x.isInternal)
|
||||||
|
for j in 0..<x.entries:
|
||||||
|
if eq(key, x.keys[j]): return x.vals[j]
|
||||||
|
return default
|
||||||
|
|
||||||
proc contains*[Key, Val](b: BTree[Key, Val], key: Key): bool =
|
proc contains*[Key, Val](b: BTree[Key, Val], key: Key): bool =
|
||||||
var x = b.root
|
var x = b.root
|
||||||
while x.isInternal:
|
while x.isInternal:
|
||||||
|
|
|
@ -18,15 +18,25 @@ type
|
||||||
RestApiCallback* = proc(request: HttpRequestRef, pathParams: HttpTable,
|
RestApiCallback* = proc(request: HttpRequestRef, pathParams: HttpTable,
|
||||||
queryParams: HttpTable,
|
queryParams: HttpTable,
|
||||||
body: Option[ContentBody]): Future[RestApiResponse] {.
|
body: Option[ContentBody]): Future[RestApiResponse] {.
|
||||||
raises: [Defect], gcsafe.}
|
raises: [Defect], gcsafe.}
|
||||||
|
|
||||||
|
RestRouteKind* {.pure.} = enum
|
||||||
|
None, Handler, Redirect
|
||||||
|
|
||||||
RestRoute* = object
|
RestRoute* = object
|
||||||
requestPath*: SegmentedPath
|
requestPath*: SegmentedPath
|
||||||
routePath*: SegmentedPath
|
routePath*: SegmentedPath
|
||||||
callback*: RestApiCallback
|
callback*: RestApiCallback
|
||||||
|
|
||||||
RestRouteItem* = object
|
RestRouteItem* = object
|
||||||
|
case kind*: RestRouteKind
|
||||||
|
of RestRouteKind.None:
|
||||||
|
discard
|
||||||
|
of RestRouteKind.Handler:
|
||||||
|
callback: RestApiCallback
|
||||||
|
of RestRouteKind.Redirect:
|
||||||
|
redirectPath*: SegmentedPath
|
||||||
path: SegmentedPath
|
path: SegmentedPath
|
||||||
callback: RestApiCallback
|
|
||||||
|
|
||||||
RestRouter* = object
|
RestRouter* = object
|
||||||
patternCallback*: PatternCallback
|
patternCallback*: PatternCallback
|
||||||
|
@ -42,18 +52,47 @@ proc init*(t: typedesc[RestRouter],
|
||||||
proc addRoute*(rr: var RestRouter, request: HttpMethod, path: string,
|
proc addRoute*(rr: var RestRouter, request: HttpMethod, path: string,
|
||||||
handler: RestApiCallback) {.raises: [Defect].} =
|
handler: RestApiCallback) {.raises: [Defect].} =
|
||||||
let spath = SegmentedPath.init(request, path, rr.patternCallback)
|
let spath = SegmentedPath.init(request, path, rr.patternCallback)
|
||||||
let route = rr.routes.getOrDefault(spath)
|
let route = rr.routes.getOrDefault(spath,
|
||||||
doAssert(isNil(route.callback), "The route is already in the routing table")
|
RestRouteItem(kind: RestRouteKind.None))
|
||||||
rr.routes.add(spath, RestRouteItem(path: spath, callback: handler))
|
case route.kind
|
||||||
|
of RestRouteKind.None:
|
||||||
|
let item = RestRouteItem(kind: RestRouteKind.Handler,
|
||||||
|
path: spath, callback: handler)
|
||||||
|
rr.routes.add(spath, item)
|
||||||
|
else:
|
||||||
|
raiseAssert("The route is already in the routing table")
|
||||||
|
|
||||||
|
proc addRedirect*(rr: var RestRouter, request: HttpMethod, srcPath: string,
|
||||||
|
dstPath: string) {.raises: [Defect].} =
|
||||||
|
let spath = SegmentedPath.init(request, srcPath, rr.patternCallback)
|
||||||
|
let dpath = SegmentedPath.init(request, dstPath, rr.patternCallback)
|
||||||
|
let route = rr.routes.getOrDefault(spath,
|
||||||
|
RestRouteItem(kind: RestRouteKind.None))
|
||||||
|
case route.kind
|
||||||
|
of RestRouteKind.None:
|
||||||
|
let item = RestRouteItem(kind: RestRouteKind.Redirect,
|
||||||
|
path: spath, redirectPath: dpath)
|
||||||
|
rr.routes.add(spath, item)
|
||||||
|
else:
|
||||||
|
raiseAssert("The route is already in the routing table")
|
||||||
|
|
||||||
proc getRoute*(rr: RestRouter,
|
proc getRoute*(rr: RestRouter,
|
||||||
spath: SegmentedPath): Option[RestRoute] {.raises: [Defect].} =
|
spath: SegmentedPath): Option[RestRoute] {.raises: [Defect].} =
|
||||||
let route = rr.routes.getOrDefault(spath)
|
var path = spath
|
||||||
if isNil(route.callback):
|
while true:
|
||||||
none[RestRoute]()
|
let route = rr.routes.getOrDefault(path,
|
||||||
else:
|
RestRouteItem(kind: RestRouteKind.None))
|
||||||
some[RestRoute](RestRoute(requestPath: spath, routePath: route.path,
|
case route.kind
|
||||||
callback: route.callback))
|
of RestRouteKind.None:
|
||||||
|
return none[RestRoute]()
|
||||||
|
of RestRouteKind.Handler:
|
||||||
|
# Route handler was found
|
||||||
|
let item = RestRoute(requestPath: path, routePath: route.path,
|
||||||
|
callback: route.callback)
|
||||||
|
return some(item)
|
||||||
|
of RestRouteKind.Redirect:
|
||||||
|
# Route redirection was found, so we perform path transformation
|
||||||
|
path = rewritePath(route.path, route.redirectPath, path)
|
||||||
|
|
||||||
iterator params*(route: RestRoute): string {.raises: [Defect].} =
|
iterator params*(route: RestRoute): string {.raises: [Defect].} =
|
||||||
var pats = route.routePath.patterns
|
var pats = route.routePath.patterns
|
||||||
|
@ -168,6 +207,29 @@ proc isPathArg(typeNode: NimNode): bool =
|
||||||
isBytesArg(typeNode) or (not(isOptionalArg(typeNode)) and
|
isBytesArg(typeNode) or (not(isOptionalArg(typeNode)) and
|
||||||
not(isSequenceArg(typeNode)))
|
not(isSequenceArg(typeNode)))
|
||||||
|
|
||||||
|
macro redirect*(router: RestRouter, meth: static[HttpMethod],
|
||||||
|
fromPath: static[string], toPath: static[string]): untyped =
|
||||||
|
## Define REST API endpoint which redirects request to different compatible
|
||||||
|
## endpoint ("/somecall" will be redirected to "/api/somecall").
|
||||||
|
let
|
||||||
|
srcPathStr = $fromPath
|
||||||
|
dstPathStr = $toPath
|
||||||
|
srcSegPath = SegmentedPath.init(meth, srcPathStr, nil)
|
||||||
|
dstSegPath = SegmentedPath.init(meth, dstPathStr, nil)
|
||||||
|
# Not sure about this, it creates HttpMethod(int).
|
||||||
|
methIdent = newLit(meth)
|
||||||
|
|
||||||
|
if not(isEqual(srcSegPath, dstSegPath)):
|
||||||
|
error("Source and destination path patterns should be equal", router)
|
||||||
|
|
||||||
|
var res = newStmtList()
|
||||||
|
res.add quote do:
|
||||||
|
`router`.addRedirect(`methIdent`, `fromPath`, `toPath`)
|
||||||
|
|
||||||
|
when defined(nimDumpRestAPI):
|
||||||
|
echo "\n", path, ": ", repr(res)
|
||||||
|
return res
|
||||||
|
|
||||||
macro api*(router: RestRouter, meth: static[HttpMethod],
|
macro api*(router: RestRouter, meth: static[HttpMethod],
|
||||||
path: static[string], body: untyped): untyped =
|
path: static[string], body: untyped): untyped =
|
||||||
## Define REST API endpoint and implementation.
|
## Define REST API endpoint and implementation.
|
||||||
|
|
|
@ -23,6 +23,10 @@ type
|
||||||
patternCb: PatternCallback
|
patternCb: PatternCallback
|
||||||
patterns*: uint64
|
patterns*: uint64
|
||||||
|
|
||||||
|
KeyValueTuple* = tuple
|
||||||
|
key: string
|
||||||
|
value: string
|
||||||
|
|
||||||
template isPattern*(spath: SegmentedPath, pos: int): bool =
|
template isPattern*(spath: SegmentedPath, pos: int): bool =
|
||||||
(spath.patterns and (1'u64 shl pos)) != 0'u64
|
(spath.patterns and (1'u64 shl pos)) != 0'u64
|
||||||
|
|
||||||
|
@ -43,6 +47,16 @@ iterator keys*(spath: SegmentedPath): string =
|
||||||
yield spath.data[index][1 .. ^2]
|
yield spath.data[index][1 .. ^2]
|
||||||
pats = pats and not(1'u64 shl index)
|
pats = pats and not(1'u64 shl index)
|
||||||
|
|
||||||
|
iterator indexes*(spath: SegmentedPath): int =
|
||||||
|
## Iterate over all patterns indexes in path ``spath``.
|
||||||
|
var pats = spath.patterns
|
||||||
|
while pats != 0'u64:
|
||||||
|
let index = firstOne(pats) - 1
|
||||||
|
doAssert(index < len(spath.data))
|
||||||
|
if index < len(spath.data):
|
||||||
|
yield index
|
||||||
|
pats = pats and not(1'u64 shl index)
|
||||||
|
|
||||||
proc getPatterns*(spath: SegmentedPath): seq[string] =
|
proc getPatterns*(spath: SegmentedPath): seq[string] =
|
||||||
## Returns all the patterns in path ``spath``.
|
## Returns all the patterns in path ``spath``.
|
||||||
var res = newSeq[string]()
|
var res = newSeq[string]()
|
||||||
|
@ -50,6 +64,53 @@ proc getPatterns*(spath: SegmentedPath): seq[string] =
|
||||||
res.add(item)
|
res.add(item)
|
||||||
res
|
res
|
||||||
|
|
||||||
|
proc isEqual*(spath1, spath2: SegmentedPath): bool =
|
||||||
|
## Returns ``true`` if both path has equal patterns (number of patterns are
|
||||||
|
## equal, and pattern names are equal).
|
||||||
|
let pats1 = spath1.getPatterns()
|
||||||
|
let pats2 = spath2.getPatterns()
|
||||||
|
if len(pats1) != len(pats2):
|
||||||
|
false
|
||||||
|
else:
|
||||||
|
for item in pats1:
|
||||||
|
if item notin pats2:
|
||||||
|
return false
|
||||||
|
true
|
||||||
|
|
||||||
|
iterator pairs*(spath: SegmentedPath, vpath: SegmentedPath): KeyValueTuple =
|
||||||
|
doAssert(len(spath.data) == len(vpath.data))
|
||||||
|
for index in spath.indexes():
|
||||||
|
yield (spath.data[index], vpath.data[index])
|
||||||
|
|
||||||
|
proc getPairs*(spath: SegmentedPath, vpath: SegmentedPath): seq[KeyValueTuple] =
|
||||||
|
var res: seq[tuple[key: string, value: string]]
|
||||||
|
for item in pairs(spath, vpath):
|
||||||
|
res.add(item)
|
||||||
|
res
|
||||||
|
|
||||||
|
proc getValue(data: seq[KeyValueTuple], key: string): Option[string] =
|
||||||
|
for item in data:
|
||||||
|
if item.key == key:
|
||||||
|
return some(item.value)
|
||||||
|
return none[string]()
|
||||||
|
|
||||||
|
proc rewritePath*(spath: SegmentedPath, dpath: SegmentedPath,
|
||||||
|
vpath: SegmentedPath): SegmentedPath =
|
||||||
|
doAssert(spath.patternsCount() == dpath.patternsCount())
|
||||||
|
let values = getPairs(spath, vpath)
|
||||||
|
if len(values) == 0:
|
||||||
|
SegmentedPath(data: dpath.data)
|
||||||
|
else:
|
||||||
|
var res = SegmentedPath(data: dpath.data)
|
||||||
|
var k = 0
|
||||||
|
for i in 0 ..< len(res.data):
|
||||||
|
if dpath.isPattern(i):
|
||||||
|
let vres = values.getValue(dpath.data[i])
|
||||||
|
doAssert(vres.isSome())
|
||||||
|
res.data[i] = vres.get()
|
||||||
|
inc(k)
|
||||||
|
res
|
||||||
|
|
||||||
proc `==`*(s1, s2: SegmentedPath): bool =
|
proc `==`*(s1, s2: SegmentedPath): bool =
|
||||||
if len(s1.data) == len(s2.data):
|
if len(s1.data) == len(s2.data):
|
||||||
if not(s1.hasPatterns()) and not(s2.hasPatterns()):
|
if not(s1.hasPatterns()) and not(s2.hasPatterns()):
|
||||||
|
@ -134,12 +195,17 @@ proc init*(st: typedesc[SegmentedPath],
|
||||||
upath: string, patternCb: PatternCallback): SegmentedPath =
|
upath: string, patternCb: PatternCallback): SegmentedPath =
|
||||||
var res = SegmentedPath(patternCb: patternCb, patterns: 0'u64)
|
var res = SegmentedPath(patternCb: patternCb, patterns: 0'u64)
|
||||||
var counter = 0
|
var counter = 0
|
||||||
|
var patterns: seq[string]
|
||||||
for item in upath.split("/"):
|
for item in upath.split("/"):
|
||||||
doAssert(counter < 64, "Path has too many segments (more then 64)")
|
doAssert(counter < 64, "Path has too many segments (more then 64)")
|
||||||
if len(item) >= 2:
|
if len(item) >= 2:
|
||||||
if item[0] == '{' and item[^1] == '}':
|
if item[0] == '{' and item[^1] == '}':
|
||||||
doAssert(len(item) > 2, "Patterns with empty names are not allowed")
|
doAssert(len(item) > 2, "Patterns with empty names are not allowed")
|
||||||
res.patterns = res.patterns or (1'u64 shl counter)
|
res.patterns = res.patterns or (1'u64 shl counter)
|
||||||
|
if item in patterns:
|
||||||
|
raiseAssert "Only unique patterns allowed in path"
|
||||||
|
else:
|
||||||
|
patterns.add(item)
|
||||||
res.data.add(item)
|
res.data.add(item)
|
||||||
inc(counter)
|
inc(counter)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -345,6 +345,22 @@ suite "REST API router & macro tests":
|
||||||
router.addRoute(MethodPost, "/unique/path/{pattern1}", apiCallback)
|
router.addRoute(MethodPost, "/unique/path/{pattern1}", apiCallback)
|
||||||
router.addRoute(MethodPost, "/unique/path/{pattern2}", apiCallback)
|
router.addRoute(MethodPost, "/unique/path/{pattern2}", apiCallback)
|
||||||
|
|
||||||
|
# Use HTTP method GET and redirect
|
||||||
|
router.addRedirect(MethodGet, "/redirect/path/1", "/unique/path/1")
|
||||||
|
router.addRedirect(MethodGet, "/redirect/path/2", "/unique/path/2")
|
||||||
|
router.addRedirect(MethodGet, "/redirect/path/{pattern1}",
|
||||||
|
"/unique/path/{pattern1}")
|
||||||
|
router.addRedirect(MethodGet, "/redirect/path/{pattern2}",
|
||||||
|
"/unique/path/{pattern2")
|
||||||
|
|
||||||
|
# Use HTTP method POST and redirect
|
||||||
|
router.addRedirect(MethodPost, "/redirect/path/1", "/unique/path/1")
|
||||||
|
router.addRedirect(MethodPost, "/redirect/path/2", "/unique/path/2")
|
||||||
|
router.addRedirect(MethodPost, "/redirect/path/{pattern1}",
|
||||||
|
"/unique/path/{pattern1}")
|
||||||
|
router.addRedirect(MethodPost, "/redirect/path/{pattern2}",
|
||||||
|
"/unique/path/{pattern2")
|
||||||
|
|
||||||
expect AssertionError:
|
expect AssertionError:
|
||||||
router.addRoute(MethodGet, "/unique/path/1", apiCallback)
|
router.addRoute(MethodGet, "/unique/path/1", apiCallback)
|
||||||
expect AssertionError:
|
expect AssertionError:
|
||||||
|
@ -354,6 +370,15 @@ suite "REST API router & macro tests":
|
||||||
expect AssertionError:
|
expect AssertionError:
|
||||||
router.addRoute(MethodGet, "/unique/path/{pattern2}", apiCallback)
|
router.addRoute(MethodGet, "/unique/path/{pattern2}", apiCallback)
|
||||||
|
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRoute(MethodGet, "/redirect/path/1", apiCallback)
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRoute(MethodGet, "/redirect/path/2", apiCallback)
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRoute(MethodGet, "/redirect/path/{pattern1}", apiCallback)
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRoute(MethodGet, "/redirect/path/{pattern2}", apiCallback)
|
||||||
|
|
||||||
expect AssertionError:
|
expect AssertionError:
|
||||||
router.addRoute(MethodPost, "/unique/path/1", apiCallback)
|
router.addRoute(MethodPost, "/unique/path/1", apiCallback)
|
||||||
expect AssertionError:
|
expect AssertionError:
|
||||||
|
@ -362,3 +387,164 @@ suite "REST API router & macro tests":
|
||||||
router.addRoute(MethodPost, "/unique/path/{pattern1}", apiCallback)
|
router.addRoute(MethodPost, "/unique/path/{pattern1}", apiCallback)
|
||||||
expect AssertionError:
|
expect AssertionError:
|
||||||
router.addRoute(MethodPost, "/unique/path/{pattern2}", apiCallback)
|
router.addRoute(MethodPost, "/unique/path/{pattern2}", apiCallback)
|
||||||
|
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRoute(MethodPost, "/redirect/path/1", apiCallback)
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRoute(MethodPost, "/redirect/path/2", apiCallback)
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRoute(MethodPost, "/redirect/path/{pattern1}", apiCallback)
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRoute(MethodPost, "/redirect/path/{pattern2}", apiCallback)
|
||||||
|
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodGet, "/unique/path/1", "/unique/1")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodGet, "/unique/path/2", "/unique/2")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodGet, "/unique/path/{pattern1}",
|
||||||
|
"/unique/{pattern1}")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodGet, "/unique/path/{pattern2}",
|
||||||
|
"/unique/{pattern2}")
|
||||||
|
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodGet, "/redirect/path/1", "/another/1")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodGet, "/redirect/path/2", "/another/2")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodGet, "/redirect/path/{pattern1}",
|
||||||
|
"/another/{pattern1}")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodGet, "/redirect/path/{pattern2}",
|
||||||
|
"/another/{pattern2}")
|
||||||
|
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodPost, "/unique/path/1", "/unique/1")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodPost, "/unique/path/2", "/unique/2")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodPost, "/unique/path/{pattern1}",
|
||||||
|
"/unique/{pattern1}")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodPost, "/unique/path/{pattern2}",
|
||||||
|
"/unique/{pattern2}")
|
||||||
|
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodPost, "/redirect/path/1", "/another/1")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodPost, "/redirect/path/2", "/another/2")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodPost, "/redirect/path/{pattern1}",
|
||||||
|
"/another/{pattern1}")
|
||||||
|
expect AssertionError:
|
||||||
|
router.addRedirect(MethodPost, "/redirect/path/{pattern2}",
|
||||||
|
"/another/{pattern2}")
|
||||||
|
|
||||||
|
test "Redirection test":
|
||||||
|
var router = RestRouter.init(testValidate)
|
||||||
|
|
||||||
|
router.api(MethodGet, "/test/empty/1") do (
|
||||||
|
) -> RestApiResponse:
|
||||||
|
return ok(ContentBody(contentType: "test/test", data: "ok-1".toBytes()))
|
||||||
|
router.api(MethodGet, "/test/empty/p1/p2/p3/1") do (
|
||||||
|
) -> RestApiResponse:
|
||||||
|
return ok(ContentBody(contentType: "test/test", data: "ok-2".toBytes()))
|
||||||
|
router.api(MethodGet, "/test/empty/p1/p2/p3/p5/p6/p7/p8/p9/1") do (
|
||||||
|
) -> RestApiResponse:
|
||||||
|
return ok(ContentBody(contentType: "test/test", data: "ok-3".toBytes()))
|
||||||
|
router.api(MethodGet, "/test/basic/path1/{smp1}") do (
|
||||||
|
smp1: int) -> RestApiResponse:
|
||||||
|
let s1 = smp1.get()
|
||||||
|
if s1 == 999999:
|
||||||
|
return ok(ContentBody(contentType: "test/test",
|
||||||
|
data: "ok-1".toBytes()))
|
||||||
|
router.api(MethodGet, "/test/basic/path1/path2/{smp1}/{smp2}") do (
|
||||||
|
smp1: int, smp2: string) -> RestApiResponse:
|
||||||
|
let s1 = smp1.get()
|
||||||
|
let s2 = smp2.get()
|
||||||
|
if (s1 == 999999) and (s2 == "string1"):
|
||||||
|
return ok(ContentBody(contentType: "test/test",
|
||||||
|
data: "ok-2".toBytes()))
|
||||||
|
router.api(MethodGet,
|
||||||
|
"/test/basic/path1/path2/path3/{smp1}/{smp2}/{smp3}") do (
|
||||||
|
smp1: int, smp2: string, smp3: seq[byte]) -> RestApiResponse:
|
||||||
|
let s1 = smp1.get()
|
||||||
|
let s2 = smp2.get()
|
||||||
|
let s3 = smp3.get()
|
||||||
|
if (s1 == 999999) and (s2 == "string1") and
|
||||||
|
(bytesToString(s3) == "bytes1"):
|
||||||
|
return ok(ContentBody(contentType: "test/test",
|
||||||
|
data: "ok-3".toBytes()))
|
||||||
|
|
||||||
|
router.redirect(MethodGet, "/api/1", "/test/empty/1")
|
||||||
|
router.redirect(MethodGet, "/api/2", "/test/empty/p1/p2/p3/1")
|
||||||
|
router.redirect(MethodGet, "/api/3",
|
||||||
|
"/test/empty/p1/p2/p3/p5/p6/p7/p8/p9/1")
|
||||||
|
router.redirect(MethodGet, "/api/basic/{smp1}",
|
||||||
|
"/test/basic/path1/{smp1}")
|
||||||
|
router.redirect(MethodGet, "/api/basic/{smp1}/{smp2}",
|
||||||
|
"/test/basic/path1/path2/{smp1}/{smp2}")
|
||||||
|
router.redirect(MethodGet, "/api/basic/{smp1}/{smp2}/{smp3}",
|
||||||
|
"/test/basic/path1/path2/path3/{smp1}/{smp2}/{smp3}")
|
||||||
|
# Patterns with mixed order
|
||||||
|
router.redirect(MethodGet, "/api/basic/{smp3}/{smp1}/{smp2}",
|
||||||
|
"/test/basic/path1/path2/path3/{smp1}/{smp2}/{smp3}")
|
||||||
|
router.redirect(MethodGet, "/api/basic/{smp2}/{smp3}/{smp1}",
|
||||||
|
"/test/basic/path1/path2/path3/{smp1}/{smp2}/{smp3}")
|
||||||
|
router.redirect(MethodGet, "/api/basic/{smp2}/{smp1}/{smp3}",
|
||||||
|
"/test/basic/path1/path2/path3/{smp1}/{smp2}/{smp3}")
|
||||||
|
router.redirect(MethodGet, "/api/basic/{smp2}/p1/p2/p3/{smp1}",
|
||||||
|
"/test/basic/path1/path2/{smp1}/{smp2}")
|
||||||
|
router.redirect(MethodGet, "/api/basic/{smp2}/p1/p2/p3/p4/p5/p6/{smp1}",
|
||||||
|
"/test/basic/path1/path2/{smp1}/{smp2}")
|
||||||
|
router.redirect(MethodGet,
|
||||||
|
"/api/basic/{smp2}/p1/p2/p3/p4/p5/p6/p7/{smp1}/p8",
|
||||||
|
"/test/basic/path1/path2/{smp1}/{smp2}")
|
||||||
|
|
||||||
|
let r1 = router.sendMockRequest(MethodGet, "http://l.to/api/1")
|
||||||
|
let r2 = router.sendMockRequest(MethodGet, "http://l.to/api/2")
|
||||||
|
let r3 = router.sendMockRequest(MethodGet, "http://l.to/api/3")
|
||||||
|
let r4 = router.sendMockRequest(MethodGet,
|
||||||
|
"http://l.to/api/basic/999999")
|
||||||
|
let r5 = router.sendMockRequest(MethodGet,
|
||||||
|
"http://l.to/api/basic/999999/string1")
|
||||||
|
let r6 = router.sendMockRequest(MethodGet,
|
||||||
|
"http://l.to/api/basic/999999/string1/0x627974657331")
|
||||||
|
let r7 = router.sendMockRequest(MethodGet,
|
||||||
|
"http://l.to/api/basic/0x627974657331/999999/string1")
|
||||||
|
let r8 = router.sendMockRequest(MethodGet,
|
||||||
|
"http://l.to/api/basic/string1/0x627974657331/999999")
|
||||||
|
let r9 = router.sendMockRequest(MethodGet,
|
||||||
|
"http://l.to/api/basic/string1/999999/0x627974657331")
|
||||||
|
let r10 = router.sendMockRequest(MethodGet,
|
||||||
|
"http://l.to/api/basic/string1/p1/p2/p3/999999")
|
||||||
|
let r11 = router.sendMockRequest(MethodGet,
|
||||||
|
"http://l.to/api/basic/string1/p1/p2/p3/p4/p5/p6/999999")
|
||||||
|
let r12 = router.sendMockRequest(MethodGet,
|
||||||
|
"http://l.to/api/basic/string1/p1/p2/p3/p4/p5/p6/p7/999999/p8")
|
||||||
|
check:
|
||||||
|
r1.isOk()
|
||||||
|
r2.isOk()
|
||||||
|
r3.isOk()
|
||||||
|
r4.isOk()
|
||||||
|
r5.isOk()
|
||||||
|
r6.isOk()
|
||||||
|
r7.isOk()
|
||||||
|
r8.isOk()
|
||||||
|
r9.isOk()
|
||||||
|
r10.isOk()
|
||||||
|
r11.isOk()
|
||||||
|
r12.isOk()
|
||||||
|
bytesToString(r1.get().data) == "ok-1"
|
||||||
|
bytesToString(r2.get().data) == "ok-2"
|
||||||
|
bytesToString(r3.get().data) == "ok-3"
|
||||||
|
bytesToString(r4.get().data) == "ok-1"
|
||||||
|
bytesToString(r5.get().data) == "ok-2"
|
||||||
|
bytesToString(r6.get().data) == "ok-3"
|
||||||
|
bytesToString(r7.get().data) == "ok-3"
|
||||||
|
bytesToString(r8.get().data) == "ok-3"
|
||||||
|
bytesToString(r9.get().data) == "ok-3"
|
||||||
|
bytesToString(r10.get().data) == "ok-2"
|
||||||
|
bytesToString(r11.get().data) == "ok-2"
|
||||||
|
bytesToString(r12.get().data) == "ok-2"
|
||||||
|
|
|
@ -53,3 +53,14 @@ suite "SegmentedPath test suite":
|
||||||
check:
|
check:
|
||||||
len(path.data) == (i + 2)
|
len(path.data) == (i + 2)
|
||||||
path.getPatterns() == names
|
path.getPatterns() == names
|
||||||
|
|
||||||
|
test "Non-unique patterns test":
|
||||||
|
const NonUniqueVectors = [
|
||||||
|
"/{item1}/{item2}/{item1}",
|
||||||
|
"/{i1}/{i1}",
|
||||||
|
"/{a}/{b}/{a}"
|
||||||
|
]
|
||||||
|
for item in NonUniqueVectors:
|
||||||
|
expect AssertionError:
|
||||||
|
let path {.used.} = SegmentedPath.init(HttpMethod.MethodGet, item,
|
||||||
|
validate)
|
||||||
|
|
Loading…
Reference in New Issue