Skip to content

Commit 9088623

Browse files
committed
feat(router): forward query string in declarative redirects
1 parent 3300013 commit 9088623

2 files changed

Lines changed: 62 additions & 3 deletions

File tree

src/router/brisk.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ import type {
2222
RouteBuilderArguments,
2323
} from '../types/url_builder.ts'
2424

25+
/**
26+
* Options for declarative route redirects
27+
*/
28+
type RedirectResponseOptions = {
29+
/** HTTP status code for the redirect response */
30+
status?: number
31+
/** Whether to forward the incoming request query string */
32+
forwardQueryString?: boolean
33+
}
34+
35+
type RedirectOptions = URLOptions & RedirectResponseOptions
36+
2537
/**
2638
* Brisk routes exposes the API to configure the route handler by chaining
2739
* one of the pre-defined methods.
@@ -102,7 +114,7 @@ export class BriskRoute extends Macroable {
102114
*/
103115
redirect<Identifier extends keyof GetRoutesForMethod<RoutesList, 'GET'> & string>(
104116
...args: RoutesList extends LookupList
105-
? RouteBuilderArguments<Identifier, RoutesList['GET'][Identifier], URLOptions>
117+
? RouteBuilderArguments<Identifier, RoutesList['GET'][Identifier], RedirectOptions>
106118
: []
107119
): Route {
108120
const [identifier, params, options] = args as any[]
@@ -112,6 +124,9 @@ export class BriskRoute extends Macroable {
112124
if (options?.status) {
113125
redirector.status(options.status)
114126
}
127+
if (options?.forwardQueryString !== undefined) {
128+
redirector.withQs(options.forwardQueryString)
129+
}
115130
return (redirector.toRoute as any)(identifier, params || ctx.params, options)
116131
}
117132

@@ -122,15 +137,18 @@ export class BriskRoute extends Macroable {
122137
/**
123138
* Redirect request to a fixed URL
124139
* @param url - The URL to redirect to
125-
* @param options - Optional redirect options including HTTP status code
140+
* @param options - Optional redirect options including HTTP status code and query string forwarding
126141
* @returns The created route instance
127142
*/
128-
redirectToPath(url: string, options?: { status: number }): Route {
143+
redirectToPath(url: string, options?: RedirectResponseOptions): Route {
129144
function redirectsToPath(ctx: HttpContext) {
130145
const redirector = ctx.response.redirect()
131146
if (options?.status) {
132147
redirector.status(options.status)
133148
}
149+
if (options?.forwardQueryString !== undefined) {
150+
redirector.withQs(options.forwardQueryString)
151+
}
134152
return redirector.toPath(url)
135153
}
136154

tests/server.spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,30 @@ test.group('Server | Response handling', () => {
323323
assert.equal(headers.location, '/dashboard')
324324
})
325325

326+
test('redirect to a route and forward the incoming query string', async ({ assert }) => {
327+
const app = new AppFactory().create(BASE_URL, () => {})
328+
const server = new ServerFactory().merge({ app }).create()
329+
const httpServer = createServer(server.handle.bind(server))
330+
331+
await app.init()
332+
333+
server.use([])
334+
335+
server
336+
.getRouter()
337+
.get('/dashboard', () => 'dashboard')
338+
.as('dashboard')
339+
340+
// @ts-expect-error "Because RoutesList" does not have this route
341+
server.getRouter().on('/').redirect('dashboard', {}, { forwardQueryString: true })
342+
343+
await server.boot()
344+
345+
const { status, headers } = await supertest(httpServer).get('/?tab=profile')
346+
assert.equal(status, 302)
347+
assert.equal(headers.location, '/dashboard?tab=profile')
348+
})
349+
326350
test('redirect to a path using route.redirectToPath method', async ({ assert }) => {
327351
const app = new AppFactory().create(BASE_URL, () => {})
328352
const server = new ServerFactory().merge({ app }).create()
@@ -341,6 +365,23 @@ test.group('Server | Response handling', () => {
341365
assert.equal(headers.location, '/dashboard')
342366
})
343367

368+
test('redirect to a path and forward the incoming query string', async ({ assert }) => {
369+
const app = new AppFactory().create(BASE_URL, () => {})
370+
const server = new ServerFactory().merge({ app }).create()
371+
const httpServer = createServer(server.handle.bind(server))
372+
373+
await app.init()
374+
375+
server.use([])
376+
server.getRouter().on('/').redirectToPath('/dashboard', { forwardQueryString: true })
377+
378+
await server.boot()
379+
380+
const { status, headers } = await supertest(httpServer).get('/?tab=profile')
381+
assert.equal(status, 302)
382+
assert.equal(headers.location, '/dashboard?tab=profile')
383+
})
384+
344385
test('redirect to a route with custom status code', async ({ assert }) => {
345386
const app = new AppFactory().create(BASE_URL, () => {})
346387
const server = new ServerFactory().merge({ app }).create()

0 commit comments

Comments
 (0)