Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set the redirect status code #82

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/modules/Middleware.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -1079,7 +1079,8 @@ Returns a middleware that sends a redirect to `uri`

```ts
export declare function redirect<E = never>(
uri: string | { href: string }
uri: string | { href: string },
code: RedirectionStatus = Status.Found
): Middleware<StatusOpen, HeadersOpen, E, void>
```

Expand Down
3 changes: 2 additions & 1 deletion docs/modules/ReaderMiddleware.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -1364,7 +1364,8 @@ Added in v0.7.3

```ts
export declare function redirect<R, E = never>(
uri: string | { href: string }
uri: string | { href: string },
code: H.RedirectionStatus = H.Status.Found
): ReaderMiddleware<R, H.StatusOpen, H.HeadersOpen, E, void>
```

Expand Down
57 changes: 34 additions & 23 deletions docs/modules/index.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Added in v0.5.0
- [HeadersOpen (interface)](#headersopen-interface)
- [MediaType](#mediatype)
- [MediaType (type alias)](#mediatype-type-alias)
- [RedirectionStatus (type alias)](#redirectionstatus-type-alias)
- [ResponseEnded (interface)](#responseended-interface)
- [Status](#status)
- [Status (type alias)](#status-type-alias)
Expand Down Expand Up @@ -789,6 +790,16 @@ export type MediaType = typeof MediaType[keyof typeof MediaType]

Added in v0.5.0

## RedirectionStatus (type alias)

**Signature**

```ts
export type RedirectionStatus = typeof RedirectionStatus[keyof typeof RedirectionStatus]
```

Added in v0.5.0

## ResponseEnded (interface)

Type indicating that headers have already been sent, and that the body stream, and thus the response, is finished
Expand All @@ -809,29 +820,6 @@ Added in v0.5.0

```ts
export declare const Status: {
readonly Continue: 100
readonly SwitchingProtocols: 101
readonly Processing: 102
readonly EarlyHints: 103
readonly OK: 200
readonly Created: 201
readonly Accepted: 202
readonly NonAuthoritativeInformation: 203
readonly NoContent: 204
readonly ResetContent: 205
readonly PartialContent: 206
readonly MultiStatus: 207
readonly AlreadyReported: 208
readonly IMUsed: 226
readonly MultipleChoices: 300
readonly MovedPermanently: 301
readonly Found: 302
readonly SeeOther: 303
readonly NotModified: 304
readonly UseProxy: 305
readonly SwitchProxy: 306
readonly TemporaryRedirect: 307
readonly PermanentRedirect: 308
readonly BadRequest: 400
readonly Unauthorized: 401
readonly PaymentRequired: 402
Expand Down Expand Up @@ -872,6 +860,29 @@ export declare const Status: {
readonly LoopDetected: 508
readonly NotExtended: 510
readonly NetworkAuthenticationRequired: 511
readonly MultipleChoices: 300
readonly MovedPermanently: 301
readonly Found: 302
readonly SeeOther: 303
readonly NotModified: 304
readonly UseProxy: 305
readonly SwitchProxy: 306
readonly TemporaryRedirect: 307
readonly PermanentRedirect: 308
readonly Continue: 100
readonly SwitchingProtocols: 101
readonly Processing: 102
readonly EarlyHints: 103
readonly OK: 200
readonly Created: 201
readonly Accepted: 202
readonly NonAuthoritativeInformation: 203
readonly NoContent: 204
readonly ResetContent: 205
readonly PartialContent: 206
readonly MultiStatus: 207
readonly AlreadyReported: 208
readonly IMUsed: 226
}
```

Expand Down
19 changes: 19 additions & 0 deletions dtslint/ts3.5/Middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as E from 'fp-ts/Either'
import * as O from 'fp-ts/Option'
import * as TO from 'fp-ts/TaskOption'
import { pipe } from 'fp-ts/function'
import * as H from '../../src'
import * as _ from '../../src/Middleware'

declare const middleware1: _.Middleware<'one', 'one', number, boolean>
Expand All @@ -10,6 +11,8 @@ declare const middleware2b: _.Middleware<'one', 'two', Error, string>
declare const middleware3: _.Middleware<'two', 'three', number, string>
declare const decoderU: (value: unknown) => E.Either<number, boolean>
declare const decoderS: (value: string) => E.Either<number, boolean>
declare const status: H.Status
declare const statusRedirection: H.RedirectionStatus

//
// decodeParam
Expand Down Expand Up @@ -106,6 +109,22 @@ pipe(
_.ichainFirstW(() => middleware3) // $ExpectError
)

//
// redirect
//

// $ExpectType Middleware<StatusOpen, HeadersOpen, never, void>
_.redirect('http://www.example.com/')

// $ExpectType Middleware<StatusOpen, HeadersOpen, Error, void>
_.redirect<Error>('http://www.example.com/')

// $ExpectType Middleware<StatusOpen, HeadersOpen, never, void>
_.redirect('http://www.example.com/', statusRedirection)

// $ExpectError
_.redirect('http://www.example.com/', status)

//
// chainOptionK
//
Expand Down
19 changes: 19 additions & 0 deletions dtslint/ts3.5/ReaderMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ declare const readerTaskEither2: ReaderTaskEither<R2, Error, string>
declare const decoderU: (value: unknown) => E.Either<number, boolean>
declare const decoderS: (value: string) => E.Either<number, boolean>

declare const status: H.Status
declare const statusRedirection: H.RedirectionStatus

//
// asksReaderMiddlewareW
//
Expand Down Expand Up @@ -369,6 +372,22 @@ pipe(
_.chainFirstReaderTaskEitherK(() => readerTaskEither2) // $ExpectError
)

//
// redirect
//

// $ExpectType ReaderMiddleware<unknown, StatusOpen, HeadersOpen, never, void>
_.redirect('http://www.example.com/')

// $ExpectType ReaderMiddleware<R1, StatusOpen, HeadersOpen, Error, void>
_.redirect<R1, Error>('http://www.example.com/')

// $ExpectType ReaderMiddleware<unknown, StatusOpen, HeadersOpen, never, void>
_.redirect('http://www.example.com/', statusRedirection)

// $ExpectError
_.redirect('http://www.example.com/', status)

//
// chainOptionK
//
Expand Down
19 changes: 16 additions & 3 deletions src/Middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,17 @@ import { Bifunctor3 } from 'fp-ts/Bifunctor'
import { identity, Lazy, pipe, Predicate, Refinement } from 'fp-ts/function'
import { Functor3, bindTo as bindTo_ } from 'fp-ts/Functor'
import { Monad3 } from 'fp-ts/Monad'
import { BodyOpen, Connection, CookieOptions, HeadersOpen, MediaType, ResponseEnded, Status, StatusOpen } from '.'
import {
BodyOpen,
Connection,
CookieOptions,
HeadersOpen,
MediaType,
RedirectionStatus,
ResponseEnded,
Status,
StatusOpen,
} from '.'
import * as T from 'fp-ts/Task'
import { IO } from 'fp-ts/IO'
import { IOEither } from 'fp-ts/IOEither'
Expand Down Expand Up @@ -616,9 +626,12 @@ export function json<E>(
* @category constructors
* @since 0.7.0
*/
export function redirect<E = never>(uri: string | { href: string }): Middleware<StatusOpen, HeadersOpen, E, void> {
export function redirect<E = never>(
uri: string | { href: string },
code: RedirectionStatus = Status.Found
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Location can also be used on a 201 Created response, but I'm not sure if that should be included here or not.

): Middleware<StatusOpen, HeadersOpen, E, void> {
return pipe(
status(Status.Found),
status(code),
ichain(() => header('Location', typeof uri === 'string' ? uri : uri.href))
)
}
Expand Down
5 changes: 3 additions & 2 deletions src/ReaderMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,9 +428,10 @@ export function json<R, E>(
* @since 0.6.3
*/
export function redirect<R, E = never>(
uri: string | { href: string }
uri: string | { href: string },
code: H.RedirectionStatus = H.Status.Found
): ReaderMiddleware<R, H.StatusOpen, H.HeadersOpen, E, void> {
return () => M.redirect(uri)
return () => M.redirect(uri, code)
}

/**
Expand Down
27 changes: 18 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ export const MediaType = {
*/
export type MediaType = typeof MediaType[keyof typeof MediaType]

const RedirectionStatus = {
MultipleChoices: 300,
MovedPermanently: 301,
Found: 302,
SeeOther: 303,
NotModified: 304,
UseProxy: 305,
SwitchProxy: 306,
TemporaryRedirect: 307,
PermanentRedirect: 308,
} as const

/**
* @since 0.5.0
*/
Expand All @@ -53,15 +65,7 @@ export const Status = {
MultiStatus: 207,
AlreadyReported: 208,
IMUsed: 226,
MultipleChoices: 300,
MovedPermanently: 301,
Found: 302,
SeeOther: 303,
NotModified: 304,
UseProxy: 305,
SwitchProxy: 306,
TemporaryRedirect: 307,
PermanentRedirect: 308,
...RedirectionStatus,
BadRequest: 400,
Unauthorized: 401,
PaymentRequired: 402,
Expand Down Expand Up @@ -109,6 +113,11 @@ export const Status = {
*/
export type Status = typeof Status[keyof typeof Status]

/**
* @since 0.5.0
*/
export type RedirectionStatus = typeof RedirectionStatus[keyof typeof RedirectionStatus]

/**
* @since 0.5.0
*/
Expand Down
24 changes: 14 additions & 10 deletions test/Middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,16 +199,20 @@ describe('Middleware', () => {

describe('redirect', () => {
it.each([
['string', '/users', '/users'],
['URL', new URL('http://example.com/users'), 'http://example.com/users'],
])('should add the correct status / header for a %s', (_type, actual, expected) => {
const m = _.redirect(actual)
const c = new MockConnection<H.StatusOpen>(new MockRequest())
return assertSuccess(m, c, undefined, [
{ type: 'setStatus', status: 302 },
{ type: 'setHeader', name: 'Location', value: expected },
])
})
['string', '/users', '/users', undefined, 302],
['URL', new URL('http://example.com/users'), 'http://example.com/users', undefined, 302],
['status code', '/users', '/users', 303, 303],
] as const)(
'should add the correct status / header for a %s',
(_type, actualUrl, expectedUrl, actualStatusCode, expectedStatusCode) => {
const m = _.redirect(actualUrl, actualStatusCode)
const c = new MockConnection<H.StatusOpen>(new MockRequest())
return assertSuccess(m, c, undefined, [
{ type: 'setStatus', status: expectedStatusCode },
{ type: 'setHeader', name: 'Location', value: expectedUrl },
])
}
)
})

describe('pipeStream', () => {
Expand Down
22 changes: 13 additions & 9 deletions test/ReaderMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,15 +316,19 @@ describe('ReaderMiddleware', () => {

describe('redirect', () => {
it.each([
['string', '/users', '/users'],
['URL', new URL('http://example.com/users'), 'http://example.com/users'],
])('should add the correct status / header for a %s', (_type, actual, expected) => {
const m1 = _.redirect(actual)
const r = 'yee'
const m2 = M.redirect(expected)
const c = new MockConnection<H.StatusOpen>(new MockRequest())
return assertProperty(m1, r, m2, c)
})
['string', '/users', '/users', undefined, 302],
['URL', new URL('http://example.com/users'), 'http://example.com/users', undefined, 302],
['status code', '/users', '/users', 303, 303],
] as const)(
'should add the correct status / header for a %s',
(_type, actualUrl, expectedUrl, actualStatusCode, expectedStatusCode) => {
const m1 = _.redirect(actualUrl, actualStatusCode)
const r = 'yee'
const m2 = M.redirect(expectedUrl, expectedStatusCode)
const c = new MockConnection<H.StatusOpen>(new MockRequest())
return assertProperty(m1, r, m2, c)
}
)
})

describe('decodeParam', () => {
Expand Down