Skip to content

Commit 736cb93

Browse files
Fix JsonView OpenAPI docs after rebase
1 parent b882630 commit 736cb93

3 files changed

Lines changed: 24 additions & 7 deletions

File tree

Guide/view.markdown

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ document.addEventListener('ihp:unload', () => {
462462

463463
## JSON
464464

465-
Views that are rendered by calling the [`render`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Render.html#v:render) function can also respond with JSON.
465+
Views that are rendered by calling the [`renderHtmlOrJson`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Render.html#v:renderHtmlOrJson) function can respond with either HTML or JSON based on the request `Accept` header.
466466

467467
Let's say we have a normal HTML view that renders all posts for our blog app:
468468

@@ -491,7 +491,7 @@ instance View IndexView where
491491
|]
492492
```
493493

494-
We can add a JSON output for all blog posts by defining a typed [`JsonResponse`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewSupport.html#t:View) payload and implementing [`jsonTyped`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewSupport.html#v:jsonTyped):
494+
We can add a JSON output for all blog posts by defining a typed [`JsonResponse`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewSupport.html#t:JsonView) payload and implementing [`jsonTyped`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewSupport.html#v:jsonTyped) in a `JsonView` instance:
495495

496496
```haskell
497497
{-# LANGUAGE DeriveGeneric #-}
@@ -515,6 +515,7 @@ instance View IndexView where
515515
...
516516
|]
517517

518+
instance JsonView IndexView where
518519
type JsonResponse IndexView = [PostPayload]
519520

520521
jsonTyped IndexView { .. } =
@@ -528,6 +529,14 @@ instance View IndexView where
528529

529530
In the above code, [`jsonTyped`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewSupport.html#v:jsonTyped) has access to all arguments passed to the view, but returns a normal Haskell value instead of raw `Value`. IHP then turns that into JSON automatically using [`toJSON`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewPrelude.html#v:toJSON).
530531

532+
In the controller, use [`renderHtmlOrJson`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Render.html#v:renderHtmlOrJson) instead of `render` for actions that should serve both formats:
533+
534+
```haskell
535+
action PostsAction = do
536+
posts <- query @Post |> fetch
537+
renderHtmlOrJson IndexView { .. }
538+
```
539+
531540
The full `Index` View for our `PostsController` looks like this:
532541

533542
```haskell
@@ -573,6 +582,7 @@ instance View IndexView where
573582
</div>
574583
|]
575584

585+
instance JsonView IndexView where
576586
type JsonResponse IndexView = [PostPayload]
577587

578588
jsonTyped IndexView { .. } =
@@ -593,7 +603,7 @@ renderPost post = [hsx|
593603
|]
594604
```
595605

596-
You can still override [`json`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewSupport.html#v:json) directly for backwards compatibility. The typed `JsonResponse` / `jsonTyped` style is preferred because it is also the representation used by IHP's OpenAPI support.
606+
You can still override [`json`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewSupport.html#v:json) directly in `JsonView` for backwards compatibility. The typed `JsonResponse` / `jsonTyped` style is preferred because it is also the representation used by IHP's OpenAPI support.
597607

598608
### Getting JSON responses
599609

@@ -641,6 +651,7 @@ instance ToSchema PostPayload
641651
instance View IndexView where
642652
html IndexView { .. } = [hsx|...|]
643653

654+
instance JsonView IndexView where
644655
type JsonResponse IndexView = [PostPayload]
645656
jsonTyped IndexView { .. } = posts |> map (\post -> PostPayload { id = post.id, title = post.title })
646657

ihp/IHP/RouterSupport.hs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ import GHC.TypeLits
9898
import GHC.TypeLits as T
9999
import IHP.Controller.Context
100100
import IHP.Controller.Param
101-
import IHP.Controller.Response (ResponseException(..))
102101
import IHP.ControllerSupport
103102
import IHP.ErrorController qualified as ErrorController
104103
import IHP.FrameworkConfig
@@ -229,6 +228,7 @@ actionDocFor ::
229228
forall actionName view controller.
230229
( KnownSymbol actionName
231230
, ViewSupport.View view
231+
, ViewSupport.JsonView view
232232
, Typeable.Typeable view
233233
, JSON.ToJSON (ViewSupport.JsonResponse view)
234234
, ToSchema (ViewSupport.JsonResponse view)
@@ -254,6 +254,7 @@ actionDocForRequestBody ::
254254
( KnownSymbol actionName
255255
, HasOpenApiRequestBody controller actionName
256256
, ViewSupport.View view
257+
, ViewSupport.JsonView view
257258
, Typeable.Typeable view
258259
, JSON.ToJSON (ViewSupport.JsonResponse view)
259260
, ToSchema (ViewSupport.JsonResponse view)
@@ -550,16 +551,16 @@ openApiRenderExpectationKey :: Vault.Key DocumentedRenderExpectation
550551
openApiRenderExpectationKey = unsafePerformIO Vault.newKey
551552
{-# NOINLINE openApiRenderExpectationKey #-}
552553

553-
throwOpenApiRenderMismatch :: Text -> IO a
554+
throwOpenApiRenderMismatch :: (?request :: Request, ?respond :: Respond) => Text -> IO a
554555
throwOpenApiRenderMismatch message =
555-
Exception.throwIO
556-
(ResponseException (responseLBS status500 [(hContentType, "text/plain")] (cs message)))
556+
respondAndExit (responseLBS status500 [(hContentType, "text/plain")] (cs message))
557557
{-# INLINE throwOpenApiRenderMismatch #-}
558558

559559
validateOpenApiRenderedView
560560
:: forall view.
561561
( Typeable.Typeable view
562562
, ?request :: Request
563+
, ?respond :: Respond
563564
)
564565
=> view
565566
-> JSON.Value

ihp/Test/Test/OpenApiSupportSpec.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ instance ToSchema CreateSessionRequest
9898
instance View BandView where
9999
html BandView{..} = [hsx||]
100100

101+
instance JsonView BandView where
101102
type JsonResponse BandView = BandPayload
102103

103104
jsonTyped BandView{..} =
@@ -110,6 +111,7 @@ instance View BandView where
110111
instance View LegacyJsonView where
111112
html LegacyJsonView = [hsx||]
112113

114+
instance JsonView LegacyJsonView where
113115
json LegacyJsonView =
114116
JSON.object
115117
[ "legacy" JSON..= True
@@ -118,6 +120,7 @@ instance View LegacyJsonView where
118120
instance View WrongJsonShapeView where
119121
html WrongJsonShapeView{..} = [hsx||]
120122

123+
instance JsonView WrongJsonShapeView where
121124
type JsonResponse WrongJsonShapeView = BandPayload
122125

123126
jsonTyped WrongJsonShapeView{..} =
@@ -135,6 +138,7 @@ instance View WrongJsonShapeView where
135138
instance View DocumentedCustomPathView where
136139
html DocumentedCustomPathView{..} = [hsx||]
137140

141+
instance JsonView DocumentedCustomPathView where
138142
type JsonResponse DocumentedCustomPathView = BandPayload
139143

140144
jsonTyped DocumentedCustomPathView{..} =
@@ -147,6 +151,7 @@ instance View DocumentedCustomPathView where
147151
instance View AckView where
148152
html AckView = [hsx||]
149153

154+
instance JsonView AckView where
150155
type JsonResponse AckView = AckPayload
151156

152157
jsonTyped AckView = AckPayload{ok = True}

0 commit comments

Comments
 (0)