Skip to content

Commit

Permalink
Rename HTTP API field names and add warnings
Browse files Browse the repository at this point in the history
sourceHtml → content
resultHtml → content

#18
  • Loading branch information
dahlia committed Nov 12, 2021
1 parent edc3f6e commit 287eb17
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 26 deletions.
13 changes: 11 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,22 @@ To be released.

The below HTTP APIs changed:

- Added a mandatory field `"content"` to requests.
- Deprecated the `"sourceHtml"` field of requests in favour of the new
`"content"` field.
- Added an optional field `"contentType"` with the default value
`"text/html"` to requests.
- Removed `"xhtml"` field in favour of new `"contentType"` field
from requests.
- Deprecated the `"xhtml"` field of requests in favour of the new
`"contentType"` field. The legacy field will be gone in the next
minor release.
In order to use XHTML mode, configure `"contentType"` field with
`"application/xhtml+xml"`.
- Added `"content"` field to responses.
- Deprecated the `"resultHtml"` field of responses in favour of the new
`"content"` field. The legacy field is not provided for non-HTML
types, and will be gone in the next minor release.
- Added `"contentType"` field to responses.
- Added `"warnings"` field to responses.

- Added `Text.Seonbi.Html.Lang` module.

Expand Down
61 changes: 52 additions & 9 deletions app/seonbi-api.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Main (main) where

import Control.Concurrent (threadDelay)
import Control.Monad
import Data.Maybe (catMaybes)
import Data.String
import Data.Version
import GHC.Exts (IsList (..))
Expand All @@ -29,15 +30,38 @@ import Text.Seonbi.Facade
import Text.Seonbi.Trie as Trie

data Input = Input
{ sourceHtml :: Text
{ source :: Text
, configuration :: Configuration IO ()
, warnings :: [Text]
} deriving (Show)

instance FromJSON Input where
parseJSON = withObject "Input" $ \ v -> do
sourceHtml' <- v .: "sourceHtml"
sourceMaybe <- v .:? "content"
(source', w1) <- case sourceMaybe of
Just s -> return (s, Nothing)
Nothing -> do
sourceHtml' <- v .:? "sourceHtml"
case sourceHtml' of
Just h -> return
( h
, Just $ "key \"sourceHtml\" is deprecated in " <>
"favour of \"content\""
)
Nothing -> fail "key \"content\" not present"
preset <- v .:? "preset"
contentType' <- v .:? "contentType" .!= "text/html"
contentTypeMaybe <- v .:? "contentType"
(contentType', w2) <- case contentTypeMaybe of
Just t -> return (t, Nothing)
Nothing -> do
xhtml <- v .:? "xhtml"
case xhtml of
Just x -> return
( if x then "application/xhtml+xml" else "text/html"
, Just $ "key \"xhtml\" is deprecated in favour of " <>
"\"contentType\""
)
Nothing -> return ("text/html", Nothing)
config <- case preset of
Just locale ->
let presets' = presets :: M.Map Text (Configuration IO ())
Expand Down Expand Up @@ -70,7 +94,11 @@ instance FromJSON Input where
, stop = stop'
, hanja = hanja'
}
return $ Input sourceHtml' $ config { contentType = contentType' }
return $ Input
{ source = source'
, configuration = config { contentType = contentType' }
, warnings = catMaybes [w1, w2]
}

instance FromJSON ContentType where
parseJSON = withText "ContentType" $ \ t ->
Expand Down Expand Up @@ -129,13 +157,28 @@ app AppOptions { allowOrigin, debugDelayMs } request respond =
inputJson <- lazyRequestBody request
threadDelay (debugDelayMs * 1000)
case eitherDecode' inputJson of
Right (Input source config) -> do
Right (Input source config warnings) -> do
result <- transformHtmlText config source
respond' status200 $ object
let type' = contentType config
let warningComments =
if Prelude.null warnings
then Data.Text.empty
else Data.Text.concat
[ "<!--\n"
, Data.Text.intercalate "\n" warnings
, "\n-->"
]
respond' status200 $ object $
[ "success" .= Bool True
, "resultHtml" .= String result
, "contentType" .= String
(contentTypeText $ contentType config)
, "content" .= String result
, "warnings" .= Array
(GHC.Exts.fromList $ String <$> warnings)
, "contentType" .= String (contentTypeText type')
]
++
[ "resultHtml" .= String (warningComments <> result)
| type' == "text/html" ||
type' == "application/xhtml+xml"
]
Left msg -> respond' status400 $ object
[ "success" .= Bool False
Expand Down
58 changes: 49 additions & 9 deletions demo/src/Demo.elm
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Demo exposing (Model, Msg(..), init, main, update, view)

import Bootstrap.Alert as Alert
import Bootstrap.Badge as Badge
import Bootstrap.Button as Button
import Bootstrap.CDN as Cdn
import Bootstrap.Form as Form
Expand Down Expand Up @@ -39,6 +40,7 @@ import Url

apiServerUrl : String
apiServerUrl =
--"http://localhost:3800/"
"https://seonbi.herokuapp.com/"


Expand Down Expand Up @@ -118,6 +120,7 @@ type alias Source =
type alias Result_ =
{ content : String
, contentType : ContentType
, warnings : List String
}


Expand Down Expand Up @@ -271,7 +274,12 @@ init _ =
}
, loading = True
, lastTransformation = Nothing
, result = Just { content = "", contentType = Html }
, result =
Just
{ content = ""
, contentType = Html
, warnings = []
}
, sourceTabState = Tab.initialState
, resultTabState = Tab.initialState
, customDictionaryVisibility = Modal.hidden
Expand Down Expand Up @@ -441,12 +449,13 @@ transform source =
( url_, input ) =
makeInput source

mkResult contentType content =
mkResult contentType content warnings =
case parseContentType contentType of
Just t ->
Just
{ contentType = t
, content = content
, warnings = warnings
}

Nothing ->
Expand All @@ -457,10 +466,13 @@ transform source =
, body = Http.jsonBody input
, expect =
Http.expectJson (EndTransform source) <|
Json.Decode.map2
Json.Decode.map3
mkResult
(Json.Decode.field "contentType" Json.Decode.string)
(Json.Decode.field "resultHtml" Json.Decode.string)
(Json.Decode.field "content" Json.Decode.string)
(Json.Decode.field "warnings" <|
Json.Decode.list Json.Decode.string
)
}


Expand All @@ -480,8 +492,8 @@ update msg model =
Nothing ->
Just
{ content = ""
, contentType =
model.source.contentType
, contentType = model.source.contentType
, warnings = []
}

r ->
Expand Down Expand Up @@ -697,9 +709,21 @@ view model =
, Grid.col []
[ Tab.config ChangeResultTab
|> Tab.items
[ viewRenderTab model
, viewCodeTab model
]
([ viewRenderTab model
, viewCodeTab model
]
++ (case model.result of
Nothing ->
[]

Just r ->
if List.isEmpty r.warnings then
[]

else
[ viewWarningsTab r.warnings ]
)
)
|> Tab.view model.resultTabState
]
]
Expand Down Expand Up @@ -917,6 +941,22 @@ viewCodeTab model =
}


viewWarningsTab : List String -> Tab.Item Msg
viewWarningsTab warnings =
Tab.item
{ id = "warnings"
, link =
Tab.link []
[ text "Warnings "
, Badge.pillDanger []
[ warnings |> List.length |> String.fromInt |> text ]
]
, pane =
Tab.pane tabPaneAttrs
[ ul [] <| List.map (\s -> li [] [ text s ]) warnings ]
}


renderMarkdown : String -> String
renderMarkdown =
Markdown.Block.parse Nothing
Expand Down
17 changes: 11 additions & 6 deletions scripts/deno/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,10 @@ export class Seonbi implements Configuration {
}

/**
* Transforms the `sourceHtml`.
* Transforms the `content`.
*/
async transform(
sourceHtml: string,
content: string,
options: Preset | Options = { preset: "ko-kr" },
): Promise<string> {
await this.start();
Expand All @@ -191,14 +191,19 @@ export class Seonbi implements Configuration {
};
const { state: perm } = await Deno.permissions.query(permDesc);
if (perm !== "granted") await Deno.permissions.request(permDesc);
const payload = { sourceHtml, ...options };
const payload = { content, ...options };
const response = await fetch(`http://${this.host}:${this.port}/`, {
method: "POST",
headers: new Headers({ "Content-Type": "application/json" }),
body: new Blob([JSON.stringify(payload)], { type: "application/json" }),
});
const result = await response.json();
if (result.success && result?.resultHtml != null) return result.resultHtml;
if (result.success && result?.content != null) {
for (const w of result.warnings) {
console.warn(w);
}
return result.content;
}
throw new Error(result?.message ?? "Unexpected error.");
}

Expand Down Expand Up @@ -472,13 +477,13 @@ export class Seonbi implements Configuration {
* after finished.
*/
export async function transform(
sourceHtml: string,
content: string,
configuration: Configuration = DEFAULT_CONFIGURATION,
options: Preset | Options = { preset: "ko-kr" },
): Promise<string> {
const i = new Seonbi(configuration);
try {
return await i.transform(sourceHtml, options);
return await i.transform(content, options);
} finally {
await i.stop();
}
Expand Down

0 comments on commit 287eb17

Please sign in to comment.