1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE NoMonomorphismRestriction #-}
3 {-# LANGUAGE OverloadedStrings #-}
4 {-# LANGUAGE StrictData #-}
5 {-# LANGUAGE TypeApplications #-}
6 {-# LANGUAGE TypeFamilies #-}
7 {-# LANGUAGE TypeOperators #-}
8 {-# OPTIONS_GHC -fno-warn-orphans #-}
11 import Control.Applicative (Applicative(..))
12 import Control.Monad (Monad(..))
14 import Data.Either (Either(..))
15 import Data.Function (($), (.))
16 import Data.Functor ((<$>))
18 import Data.Maybe (Maybe(..))
19 import Data.Proxy (Proxy(..))
20 import Data.Semigroup (Semigroup(..))
21 import Data.String (IsString(..))
22 import Prelude (error, (+), (*))
24 import Text.Read (readMaybe)
25 import Text.Show (Show(..))
26 import qualified Data.ByteString.Lazy as BSL
27 import qualified Data.Text as Text
28 import qualified Data.Text.Encoding as Text
29 import qualified Data.Text.Lazy as TL
30 import qualified Data.Text.Lazy.Encoding as TL
31 import qualified Data.Time as Time
32 import qualified Network.HTTP.Client as Client
33 import qualified Network.HTTP.Media as Media
34 import qualified Network.HTTP.Types as HTTP
35 import qualified Network.URI as URI
36 import qualified Network.Wai.Handler.Warp as Warp
37 import qualified Test.Hspec.Wai as Wai
38 import qualified Web.HttpApiData as Web
42 import Test.Tasty.Hspec
49 | API0_Time Time.ZonedTime
52 instance IsString Time.TimeZone where
53 fromString s = case s of
54 "CET" -> Time.TimeZone (1*60) False "CET"
55 "CEST" -> Time.TimeZone (2*60) False "CEST"
56 _ -> error "unknown TimeZone"
57 instance Web.FromHttpApiData Time.TimeZone where
59 "CET" -> Right $ Time.TimeZone (1*60) True "CET"
60 "CEST" -> Right $ Time.TimeZone (2*60) False "CEST"
61 _ -> Left "unknown TimeZone"
62 instance Web.ToHttpApiData Time.TimeZone where
63 toUrlPiece (Time.TimeZone _s _b n) = Text.pack n
65 manager :: IO Client.Manager
66 manager = Client.newManager Client.defaultManagerSettings
67 Just baseURI = URI.parseURI "http://localhost:8080"
68 cliEnv = clientEnv <$> manager <*> pure baseURI
71 = "time" </> capture @Time.TimeZone "timezone"
72 <.> get @TL.Text @PlainText
74 <!> "date" </> get @TL.Text @PlainText
76 <!> "echo" </> captureAll
77 <.> get @TL.Text @PlainText
79 <!> "succ" </> capture @Int "n"
80 <.> get @Int @PlainText
82 <!> "info" </> ( head @TL.Text @PlainText
83 <!> get @TL.Text @PlainText
85 instance MimeSerialize () PlainText where
86 mimeSerialize _mt = fromString . show
87 instance MimeUnserialize () PlainText where
88 mimeUnserialize _mt s =
91 _ -> Left "cannot parse ()"
93 instance MimeSerialize Int PlainText where
94 mimeSerialize _mt = BSL.fromStrict . Text.encodeUtf8 . Text.pack . show
95 instance MimeUnserialize Int PlainText where
96 mimeUnserialize _mt s =
97 case readMaybe $ TL.unpack $ TL.decodeUtf8 s of
99 _ -> Left "cannot parse Int"
118 route_time tz (ServerResponseArg respond) =
119 ServerResponse $ \_req res -> do
120 time <- Time.utcToZonedTime tz <$> Time.getCurrentTime
121 res $ respond status200 [] $
122 TL.pack $ show time <> "\n"
124 route_date (ServerResponseArg respond) =
125 ServerResponse $ \_req res -> do
126 date <- Time.utctDay <$> Time.getCurrentTime
127 res $ respond status200 [] $
128 TL.pack $ show date <> "\n"
130 route_echo path (ServerResponseArg respond) =
131 ServerResponse $ \_req res -> do
132 res $ respond status200 [] $ TL.pack $ show path <> "\n"
134 route_succ n (ServerResponseArg respond) =
135 ServerResponse $ \_req res -> do
136 res $ respond status200 [] $ n+1
138 route_info = route_head :!: route_get
140 route_head (ServerResponseArg respond) =
141 ServerResponse $ \req res -> do
142 res $ respond (HTTP.mkStatus 201 "") [] $ TL.pack $ show req <> "\n"
144 route_get (ServerResponseArg respond) =
145 ServerResponse $ \req res -> do
146 res $ respond status200 [] $ TL.pack $ show req <> "\n"
149 warp1 = Warp.run 8080 srv1
151 api2 = "ti"</>"me" </> get @TL.Text @PlainText
152 <!> "auth" </> basicAuth @User "realm" ()
153 <.> get @TL.Text @PlainText
154 <!> "da"</>"te" </> get @TL.Text @PlainText
158 instance ServerBasicAuthable context User where
159 serverBasicAuthable context user pass =
160 return $ BasicAuth_Authorized User
167 route_time (ServerResponseArg respond) =
168 ServerResponse $ \_req res -> do
169 time <- Time.getCurrentTime
170 res $ respond status200 [] $
171 TL.pack $ show time <> "\n"
173 route_date (ServerResponseArg respond) =
174 ServerResponse $ \_req res -> do
175 date <- Time.utctDay <$> Time.getCurrentTime
176 res $ respond status200 [] $
177 TL.pack $ show date <> "\n"
179 route_auth User (ServerResponseArg respond) =
180 ServerResponse $ \_req res -> do
181 res $ respond status200 [] $
182 TL.pack $ show User <> "\n"
185 warp2 = Warp.run 8080 srv2
189 Wai.with (return srv2) $ do
190 it "respond with success" $ do
192 `Wai.shouldRespondWith` 200
193 it "checks Accept header" $ do
194 Wai.request HTTP.methodGet "/da/te"
195 [ (HTTP.hAccept, Media.renderHeader $ mimeType $ Proxy @OctetStream)
197 `Wai.shouldRespondWith` 406