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]
86 instance MimeEncodable Int PlainText where
87 mimeEncode _mt = BSL.fromStrict . Text.encodeUtf8 . Text.pack . show
88 instance MimeDecodable Int PlainText where
90 case readMaybe $ TL.unpack $ TL.decodeUtf8 s of
92 _ -> Left "cannot parse Int"
111 route_time tz (ServerRespond respond) =
112 ServerResponse $ \_req res -> do
113 time <- Time.utcToZonedTime tz <$> Time.getCurrentTime
114 res $ respond status200 [] $
115 TL.pack $ show time <> "\n"
117 route_date (ServerRespond respond) =
118 ServerResponse $ \_req res -> do
119 date <- Time.utctDay <$> Time.getCurrentTime
120 res $ respond status200 [] $
121 TL.pack $ show date <> "\n"
123 route_echo path (ServerRespond respond) =
124 ServerResponse $ \_req res -> do
125 res $ respond status200 [] $ TL.pack $ show path <> "\n"
127 route_succ n (ServerRespond respond) =
128 ServerResponse $ \_req res -> do
129 res $ respond status200 [] $ n+1
131 route_info = route_head :!: route_get
133 route_head (ServerRespond respond) =
134 ServerResponse $ \req res -> do
135 res $ respond (HTTP.mkStatus 201 "") [] $ TL.pack $ show req <> "\n"
137 route_get (ServerRespond respond) =
138 ServerResponse $ \req res -> do
139 res $ respond status200 [] $ TL.pack $ show req <> "\n"
142 warp1 = Warp.run 8080 srv1
144 api2 = "ti"</>"me" </> get @TL.Text @'[PlainText]
145 <!> "auth" </> basicAuth @User "realm"
146 <.> get @TL.Text @'[PlainText]
147 <!> "da"</>"te" </> get @TL.Text @'[PlainText]
151 instance ServerBasicAuth User where
152 serverBasicAuth user pass =
153 return $ BasicAuth_Authorized User
160 route_time (ServerRespond respond) =
161 ServerResponse $ \_req res -> do
162 time <- Time.getCurrentTime
163 res $ respond status200 [] $
164 TL.pack $ show time <> "\n"
166 route_date (ServerRespond respond) =
167 ServerResponse $ \_req res -> do
168 date <- Time.utctDay <$> Time.getCurrentTime
169 res $ respond status200 [] $
170 TL.pack $ show date <> "\n"
172 route_auth User (ServerRespond respond) =
173 ServerResponse $ \_req res -> do
174 res $ respond status200 [] $
175 TL.pack $ show User <> "\n"
178 warp2 = Warp.run 8080 srv2
182 Wai.with (return srv2) $ do
183 it "respond with success" $ do
185 `Wai.shouldRespondWith` 200
186 it "checks Accept header" $ do
187 Wai.request HTTP.methodGet "/da/te"
188 [ (HTTP.hAccept, Media.renderHeader $ mediaType $ Proxy @OctetStream)
190 `Wai.shouldRespondWith` 406