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

FBCM-5157 Allow multiple SetCookie headers in response #197

Merged
merged 9 commits into from
Nov 17, 2022
5 changes: 4 additions & 1 deletion src/HTTPure/Request.purs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import HTTPure.Headers (Headers)
import HTTPure.Headers (read) as Headers
import HTTPure.Method (Method)
import HTTPure.Method (read) as Method
import HTTPure.MultiHeaders (MultiHeaders)
import HTTPure.MultiHeaders as HTTPure.MultiHeaders
import HTTPure.Path (Path)
import HTTPure.Path (read) as Path
import HTTPure.Query (Query)
Expand All @@ -33,6 +35,7 @@ type Request =
, path :: Path
, query :: Query
, headers :: Headers
, multiHeaders :: MultiHeaders
, body :: RequestBody
, httpVersion :: Version
, url :: String
Expand Down Expand Up @@ -60,8 +63,8 @@ fromHTTPRequest request = do
, path: Path.read request
, query: Query.read request
, headers: Headers.read request
, multiHeaders: HTTPure.MultiHeaders.read request
, body
, httpVersion: Version.read request
, url: requestURL request
}

7 changes: 6 additions & 1 deletion src/HTTPure/Response.purs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ import Effect.Class (class MonadEffect, liftEffect)
import HTTPure.Body (class Body, defaultHeaders, write)
import HTTPure.Headers (Headers, empty)
import HTTPure.Headers (write) as Headers
import HTTPure.MultiHeaders (MultiHeaders)
import HTTPure.MultiHeaders as HTTPure.MultiHeaders
import HTTPure.Status (Status)
import HTTPure.Status
( accepted
Expand Down Expand Up @@ -216,16 +218,18 @@ type ResponseM = Aff Response
type Response =
{ status :: Status
, headers :: Headers
, multiHeaders :: MultiHeaders
, writeBody :: HTTP.Response -> Aff Unit
}

-- | Given an HTTP `Response` and a HTTPure `Response`, this method will return
-- | a monad encapsulating writing the HTTPure `Response` to the HTTP `Response`
-- | and closing the HTTP `Response`.
send :: forall m. MonadEffect m => MonadAff m => HTTP.Response -> Response -> m Unit
send httpresponse { status, headers, writeBody } = do
send httpresponse { status, headers, multiHeaders, writeBody } = do
liftEffect $ Status.write httpresponse status
liftEffect $ Headers.write httpresponse headers
liftEffect $ HTTPure.MultiHeaders.write httpresponse multiHeaders
arthurxavierx marked this conversation as resolved.
Show resolved Hide resolved
liftAff $ writeBody httpresponse

-- | For custom response statuses or providing a body for response codes that
Expand All @@ -247,6 +251,7 @@ response' status headers body = liftEffect do
pure
{ status
, headers: defaultHeaders' <> headers
, multiHeaders: HTTPure.MultiHeaders.empty
, writeBody: write body
}

Expand Down
10 changes: 9 additions & 1 deletion test/Test/HTTPure/RequestSpec.purs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Foreign.Object (singleton)
import HTTPure.Body (toString)
import HTTPure.Headers (headers)
import HTTPure.Method (Method(Post))
import HTTPure.MultiHeaders as HTTPure.MultiHeaders
import HTTPure.Request (fromHTTPRequest, fullPath)
import HTTPure.Version (Version(HTTP1_1))
import Test.HTTPure.TestHelpers (Test, mockRequest, (?=))
Expand All @@ -27,14 +28,21 @@ fromHTTPRequestSpec =
it "contains the correct headers" do
mock <- mockRequest'
mock.headers ?= headers mockHeaders
it "contains the correct multi-headers" do
mock <- mockRequest'
mock.multiHeaders ?= HTTPure.MultiHeaders.headers mockHeaders
it "contains the correct body" do
mockBody <- mockRequest' >>= _.body >>> toString
mockBody ?= "body"
it "contains the correct httpVersion" do
mock <- mockRequest'
mock.httpVersion ?= HTTP1_1
where
mockHeaders = [ Tuple "Test" "test" ]
mockHeaders =
[ Tuple "Test" "test"
, Tuple "TestMulti" "test1"
, Tuple "TestMulti" "test2"
]

mockHTTPRequest = mockRequest "1.1" "POST" "/test?a=b" "body" mockHeaders

Expand Down
11 changes: 11 additions & 0 deletions test/Test/HTTPure/ResponseSpec.purs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Effect.Aff (makeAff, nonCanceler)
import Effect.Class (liftEffect)
import HTTPure.Body (defaultHeaders)
import HTTPure.Headers (header)
import HTTPure.MultiHeaders as HTTPure.MultiHeaders
import HTTPure.Response (emptyResponse, emptyResponse', response, response', send)
import Node.Encoding (Encoding(UTF8))
import Node.HTTP (responseAsStream)
Expand All @@ -15,6 +16,7 @@ import Test.HTTPure.TestHelpers
( Test
, getResponseBody
, getResponseHeader
, getResponseMultiHeader
, getResponseStatus
, mockResponse
, (?=)
Expand All @@ -28,6 +30,9 @@ sendSpec =
mockResponse' =
{ status: 123
, headers: header "Test" "test"
, multiHeaders:
HTTPure.MultiHeaders.header "Set-Cookie" "test1"
<> HTTPure.MultiHeaders.header "Set-Cookie" "test2"
, writeBody:
\response -> makeAff \done -> do
stream <- pure $ responseAsStream response
Expand All @@ -40,6 +45,12 @@ sendSpec =
send httpResponse mockResponse'
pure $ getResponseHeader "Test" httpResponse
header ?= "test"
it "writes the multi-headers" do
header <- do
httpResponse <- liftEffect mockResponse
send httpResponse mockResponse'
pure $ getResponseMultiHeader "Set-Cookie" httpResponse
header ?= [ "test1", "test2" ]
it "writes the status" do
status <- do
httpResponse <- liftEffect mockResponse
Expand Down
3 changes: 2 additions & 1 deletion test/Test/HTTPure/TestHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export const mockRequestImpl = httpVersion => method => url => body => headers =
});
stream.method = method;
stream.url = url;
stream.headers = headers;
stream.headers = Object.fromEntries(Object.entries(headers).map(([key, values]) => [key, values[values.length - 1]]));
stream.headersDistinct = headers;
stream.httpVersion = httpVersion;

return stream;
Expand Down
15 changes: 12 additions & 3 deletions test/Test/HTTPure/TestHelpers.purs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Test.HTTPure.TestHelpers where
import Prelude

import Data.Array (fromFoldable) as Array
import Data.Array.NonEmpty (NonEmptyArray)
import Data.Either (Either(Right))
import Data.List (List(Nil, Cons), reverse)
import Data.Maybe (fromMaybe)
Expand All @@ -14,7 +15,7 @@ import Effect.Aff (Aff, makeAff, nonCanceler)
import Effect.Class (liftEffect)
import Effect.Ref (modify_, new, read)
import Foreign.Object (Object, lookup)
import Foreign.Object (fromFoldable) as Object
import Foreign.Object as Object
import Node.Buffer (Buffer, concat, create, fromString)
import Node.Buffer (toString) as Buffer
import Node.Encoding (Encoding(UTF8))
Expand Down Expand Up @@ -204,7 +205,7 @@ foreign import mockRequestImpl ::
String ->
String ->
String ->
Object String ->
Object (NonEmptyArray String) ->
Effect Request

-- | Mock an HTTP Request object
Expand All @@ -215,7 +216,11 @@ mockRequest ::
String ->
Array (Tuple String String) ->
Aff Request
mockRequest httpVersion method url body = liftEffect <<< mockRequestImpl httpVersion method url body <<< Object.fromFoldable
mockRequest httpVersion method url body =
liftEffect
<<< mockRequestImpl httpVersion method url body
<<< Object.fromFoldableWith (flip append)
<<< map (map pure)

-- | Mock an HTTP Response object
foreign import mockResponse :: Effect HTTP.Response
Expand All @@ -237,5 +242,9 @@ getResponseHeaders = unsafeCoerce <<< _.headers <<< unsafeCoerce
getResponseHeader :: String -> HTTP.Response -> String
getResponseHeader header = fromMaybe "" <<< lookup header <<< getResponseHeaders

-- | Get the current value for the multi-header on the HTTP Response object.
getResponseMultiHeader :: String -> HTTP.Response -> Array String
getResponseMultiHeader header = fromMaybe [] <<< lookup header <<< _.headers <<< unsafeCoerce

-- | Create a stream out of a string.
foreign import stringToStream :: String -> Readable ()