apiary 1.1.3 → 1.1.4
raw patch · 9 files changed
+46/−18 lines, 9 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Control.Monad.Apiary.Action: getExt :: (Has e exts, Monad m) => proxy e -> ActionT exts prms m e
+ Control.Monad.Apiary.Action: devFile :: MonadIO m => FilePath -> ActionT exts prms m ()
+ Control.Monad.Apiary.Action: devFile' :: MonadIO m => FilePath -> ActionT exts prms m ()
+ Data.Apiary.Extension: class MonadHas e m
+ Data.Apiary.Extension: getExt :: MonadHas e m => proxy e -> m e
Files
- CHANGELOG.md +5/−0
- apiary.cabal +2/−2
- src/Control/Monad/Apiary/Action.hs +2/−1
- src/Control/Monad/Apiary/Action/Internal.hs +18/−4
- src/Control/Monad/Apiary/Internal.hs +6/−1
- src/Data/Apiary/Document/Html.hs +4/−6
- src/Data/Apiary/Extension.hs +5/−0
- src/Web/Apiary.hs +3/−3
- static/api-documentation.min.js +1/−1
CHANGELOG.md view
@@ -1,3 +1,8 @@+# 1.1.4+* enhance API documentation.+* add MonadHas class.+* add devFile action.+ # 1.1.3 * wildcard, parameter Accept header.
apiary.cabal view
@@ -1,5 +1,5 @@ name: apiary-version: 1.1.3+version: 1.1.4 synopsis: Simple and type safe web framework that can be automatically generate API documentation. description: Simple and type safe web framework that can be automatically generate API documentation.@@ -40,7 +40,7 @@ . * Auto generate API documentation(example: <http://best-haskell.herokuapp.com/api/documentation>). .- more examples: <https://github.com/philopon/apiary/blob/v1.1.3/examples/>+ more examples: <https://github.com/philopon/apiary/blob/v1.1.4/examples/> . live demo: <http://best-haskell.herokuapp.com/> (source code: <https://github.com/philopon/best-haskell>)
src/Control/Monad/Apiary/Action.hs view
@@ -22,6 +22,7 @@ , showing , string, char , file+ , devFile -- ** monolithic action -- *** redirect@@ -38,12 +39,12 @@ , getRequest , getHeaders , getParams- , getExt , getQueryParams , getReqBodyParams , getReqBodyFiles -- ** setter , file'+ , devFile' , stream , rawResponse , StreamingBody
src/Control/Monad/Apiary/Action/Internal.hs view
@@ -353,6 +353,9 @@ ask = lift ask local f = hoistActionT $ local f +instance (Monad m, Has e exts) => MonadHas e (ActionT exts prms m) where+ getExt p = liftM (getExtension p . actionExts) getEnv+ -------------------------------------------------------------------------------- getEnv :: Monad m => ActionT exts prms m (ActionEnv exts)@@ -368,10 +371,6 @@ getConfig :: Monad m => ActionT exts prms m ApiaryConfig getConfig = liftM actionConfig getEnv --- | get extension.-getExt :: (Has e exts, Monad m) => proxy e -> ActionT exts prms m e-getExt p = liftM (getExtension p . actionExts) getEnv- getParams :: Monad m => ActionT exts prms m (Dict prms) getParams = ActionT $ \d _ s c -> c d s @@ -573,6 +572,21 @@ contentType (mime f) maybe (return ()) (addHeader "Last-Modified" . formatHTTPDate) t file' f p++{-# WARNING devFile' "use file' in production." #-}+devFile' :: MonadIO m => FilePath -> ActionT exts prms m ()+devFile' f = liftIO (fileExist f) >>= \e ->+ if e+ then liftIO (L.readFile f) >>= lazyBytes+ else mzero++-- | send file contents as lazy bytestring response. since v1.1.4.+{-# WARNING devFile "use file in production." #-}+devFile :: MonadIO m => FilePath -> ActionT exts prms m ()+devFile f = do+ mime <- mimeType <$> getConfig+ contentType (mime f)+ devFile' f -- | append response body from builder. since 0.1.0.0. builder :: Monad m => Builder -> ActionT exts prms m ()
src/Control/Monad/Apiary/Internal.hs view
@@ -220,14 +220,19 @@ liftBaseWith = defaultLiftBaseWith StMApiary' restoreM = defaultRestoreM unStMApiary' +instance (Has e exts, Monad actM) => MonadHas e (ApiaryT exts prms actM m) where+ getExt p = getExtension p . envExts <$> getApiaryEnv+ {-# INLINE getExt #-}+ -------------------------------------------------------------------------------- getApiaryEnv :: Monad actM => ApiaryT exts prms actM m (ApiaryEnv exts prms actM) getApiaryEnv = ApiaryT $ \env cont -> cont env mempty +{-# DEPRECATED apiaryExt "use getExt" #-} -- | get Apiary extension. apiaryExt :: (Has e exts, Monad actM) => proxy e -> ApiaryT exts prms actM m e-apiaryExt p = getExtension p . envExts <$> getApiaryEnv+apiaryExt = getExt -- | get Apiary configuration. apiaryConfig :: Monad actM => ApiaryT exts prms actM m ApiaryConfig
src/Data/Apiary/Document/Html.hs view
@@ -11,6 +11,7 @@ ) where import Language.Haskell.TH+import Language.Haskell.TH.Syntax(addDependentFile) import Data.Monoid hiding (Any) import Data.Default.Class@@ -106,10 +107,8 @@ [ H.title (toHtml documentTitle) , if documentUseCDN then cdns else embeds , $(runIO (readFile "static/jquery.cookie-1.4.1.min.js") >>= \c -> [|H.script $ preEscapedToHtml (c::String)|])--- , H.script "" ! A.src "/static/api-documentation.js"--- , H.link ! A.rel "stylesheet" ! A.href "/static/api-documentation.css"- , $(runIO (readFile "static/api-documentation.min.js") >>= \c -> [|H.script $ preEscapedToHtml (c::String)|])- , $(runIO (readFile "static/api-documentation.min.css") >>= \c -> [|H.style $ preEscapedToHtml (c::String)|])+ , $(addDependentFile "static/api-documentation.min.js" >> runIO (readFile "static/api-documentation.min.js") >>= \c -> [|H.script $ preEscapedToHtml (c::String)|])+ , $(addDependentFile "static/api-documentation.min.css" >> runIO (readFile "static/api-documentation.min.css") >>= \c -> [|H.style $ preEscapedToHtml (c::String)|]) , maybe mempty analytics documentGoogleAnalytics ] @@ -151,8 +150,7 @@ ] action (MethodDoc qs pc a d) = H.div ! A.class_ "action col-sm-offset-1 col-md-offset-1" $ mconcat- [ preconds $ maybe pc (\ac -> ("Accept: " <> toHtml (T.decodeUtf8 ac)) : pc) a- , maybe mempty (\ac -> H.script ! A.class_ "accept" ! A.type_ "text/plain" $ (toHtml $ T.decodeUtf8 ac)) a+ [ preconds $ maybe pc (\ac -> H.span ("Accept: " <> H.span (toHtml $ T.decodeUtf8 ac)) ! A.class_ "precondition-accept" : pc) a , H.div (H.p $ toHtml d) , queriesH qs ]
src/Data/Apiary/Extension.hs view
@@ -1,10 +1,12 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE ConstraintKinds #-} module Data.Apiary.Extension ( Has(getExtension)+ , MonadHas(..) , Extension(..) , Extensions , noExtension@@ -19,6 +21,9 @@ ) where import Data.Apiary.Extension.Internal++class MonadHas e m where+ getExt :: proxy e -> m e type Initializer' m a = forall i. Initializer m i (a ': i)
src/Web/Apiary.hs view
@@ -7,7 +7,7 @@ -- | Method(..) , module Data.Apiary.Method- -- | Has, Extensions, Initializer, Initializer', (+>)+ -- | Has, MonadHas, Extensions, Initializer, Initializer', (+>) , module Data.Apiary.Extension -- | key, Member, Members, NotMember, Elem((:=)) , module Data.Apiary.Dict@@ -54,7 +54,7 @@ , text, lazyText , showing , string, char- , file+ , file, devFile , redirect, redirectPermanently, redirectTemporary , defaultDocumentationAction , DefaultDocumentConfig(..)@@ -82,7 +82,7 @@ ) import Data.Apiary.Method(Method(..))-import Data.Apiary.Extension(Has, Extensions, Initializer, Initializer', (+>))+import Data.Apiary.Extension(Has, MonadHas(..), Extensions, Initializer, Initializer', (+>)) import Data.Apiary.Dict(key, Member, Members, NotMember, Elem((:=))) import Network.HTTP.Types.Status hiding (mkStatus)
static/api-documentation.min.js view
@@ -1,1 +1,1 @@-"use strict";$.cookie.json=true;$.cookie.defaults.path=window.location.pathname;$.cookie.defaults.expires=365;(function(){function get_panel_ids_common(selector){var ids=[];$(selector).each(function(){ids.push($(this).attr("id"))});return ids}function get_opened_panel_ids(){return get_panel_ids_common(".collapse.in")}function get_all_panel_ids(){return get_panel_ids_common(".collapse")}function open_panel_by_id(id){$('[id="'+id+'"]').addClass("in")}function save_panel_state(){$.cookie("panel",get_opened_panel_ids())}function restore_panel_state(){$.each($.cookie("panel")||[],function(){open_panel_by_id(this)})}function close_all_panels(){$(".collapse.in").collapse("hide");save_panel_state([])}function open_all_panels(){$(".collapse").not(".in").collapse("show");save_panel_state(get_all_panel_ids())}function toggle_all_panels(){get_opened_panel_ids().length==0?open_all_panels():close_all_panels()}$(function(){restore_panel_state();$(".collapse").on("shown.bs.collapse",save_panel_state);$(".collapse").on("hidden.bs.collapse",save_panel_state);$("h1").click(toggle_all_panels)})})();if(location.protocol.slice(0,4)=="http"){(function(){$(function(){var update_test_route_functions=[];var update_test_query_functions=[];$(".panel").each(function(){var panel=$(this);var panel_id=panel.find(".panel-collapse").attr("id");panel.find(".route-parameters tr:first").append($("<th>value</th>"));panel.find(".route-parameters tr:gt(0)").append($('<td><input class="form-control input-sm" /></td>'));panel.find(".query-parameters tr:first").append($("<th>value</th>"));panel.find(".query-parameters tr:gt(0)").append($('<td><div class="input-group"><span class="input-group-addon"><input type="checkbox"/></span><input class="form-control input-sm"/></div></td>'));panel.find(".method").each(function(){var method=$(this);var h4=method.find("h4:first");var method_str=h4.text();var div=$('<div class="request-test">');method.find("h4:first").replaceWith(div);div.append($('<button class="test-method btn btn-default">'+method_str+"</button>"));var span=$('<span class="route-string">');div.append(span);span.append($('<span class="route-url"></span>'));span.append($('<span class="route-params"></span>'));var callout=$('<div class="hidden request-result bs-callout"><div class="clearfix title"><h4 class="pull-left"></h4></div><div class="well headers"><pre><code></code></pre></div><div class="body"><pre><code></code></pre></div></div>');var close=$('<div class="close-btn pull-right">×</div>');callout.find(".title").append(close);close.click(function(){callout.addClass("hidden")});method.find(".action:last").append(callout)});var route_params={};panel.find(".route-parameters tr:gt(0)").each(function(){var p=$(this);route_params[p.find("td:first").text()]=p.find("td:last input")});function get_route(title){var ret="";title.children().each(function(){var p=$(this);if(p.hasClass("splitter")){ret+="/"}else if(p.hasClass("path")){ret+=p.text()}else if(p.hasClass("fetch")){ret+=route_params[p.text()].val()}});return ret}var route_template=panel.find(".panel-title");var route_url=panel.find(".route-url");function update_test_route(){var route=get_route(route_template);route_url.text(route)}update_test_route_functions.push(update_test_route);function save_route_parameters(){var save={};$.each(route_params,function(k,v){save[k]=v.val()});var rp=$.cookie("route")||{};rp[panel_id]=save;$.cookie("route",rp)}panel.find(".route-parameters tr:gt(0) input").keyup(update_test_route).change(save_route_parameters);panel.find(".method").each(function(){var method=$(this);var method_str=method.find(".test-method").text();var query_params={};method.find(".query-parameters tr:gt(0)").each(function(){var p=$(this);var checkbox=p.find('td:last input[type="checkbox"]');var input=p.find("td:last input.form-control");query_params[p.find("td:first").text()]={checkbox:checkbox[0],input:input}});function get_query_param(){var ret="";$.each(query_params,function(name,value){if(value.checkbox.checked){var val=value.input.val();if(val.length==0){ret+="&"+name}else{$.each(val.split(" "),function(){ret+="&"+name+"="+this})}}});if(ret.length>0){ret="?"+ret.slice(1)}return ret}var query_param=method.find(".route-params");function update_query_param(){query_param.text(get_query_param())}update_test_query_functions.push(update_query_param);function save_query_param(){var save={};$.each(query_params,function(name,value){save[name]={checkbox:value.checkbox.checked,value:value.input.val()}});var qp=$.cookie("query")||{};if(!qp[panel_id]){qp[panel_id]={}}qp[panel_id][method_str]=save;$.cookie("query",qp)}function update_and_save_query_param(){update_query_param();save_query_param()}method.find('.query-parameters input[type="checkbox"]').change(update_and_save_query_param);method.find(".query-parameters input.form-control").keyup(update_query_param).change(save_query_param);var method_btn=method.find(".test-method");var route_url=method.find(".route-url");var result=method.find(".request-result");var accept=method.find(".accept");function request_test(){if(method_btn.hasClass("disabled")){return}var url=route_url.text()+query_param.text();var meth=method_str=="*"?"GET":method_str;method_btn.addClass("disabled");function request_callback(data,status,xhr){var ct=xhr.getResponseHeader("Content-Type");var code=xhr.status;result.find("h4").text(code+" "+status+' "'+ct+'"');result.find(".headers code").text(xhr.getAllResponseHeaders());if(ct.slice(0,16)=="application/json"){try{var json=JSON.stringify(JSON.parse(data),undefined,2);result.find(".body code").text(json)}catch(e){result.find(".body code").text(e+"\n\n"+data)}}else{result.find(".body code").text(data)}}function beforeSend(xhr){if(accept.length==1){xhr.setRequestHeader("Accept",accept.text())}}$.ajax({type:meth,url:url,dataType:"text",beforeSend:beforeSend}).done(function(data,status,xhr){request_callback(data,status,xhr);result.addClass("bs-callout-primary");result.removeClass("bs-callout-danger")}).fail(function(xhr,status,err){request_callback(err,status,xhr);result.addClass("bs-callout-danger");result.removeClass("bs-callout-primary")}).always(function(){result.removeClass("hidden");method_btn.removeClass("disabled")})}method.find(".test-method").click(request_test)})});function restore_route_parameters(){$.each($.cookie("route")||{},function(id,dict){var panel=$('[id="'+id+'"]');panel.find(".route-parameters tr:gt(0)").each(function(){var row=$(this);var key=row.find("td:first").text();row.find("td:last input").val(dict[key]||"")})})}function restore_query_parameters(){$.each($.cookie("query")||{},function(id,dict){var panel=$('[id="'+id+'"]');panel.find(".method").each(function(){var method=$(this);var method_str=method.find(".test-method").text();method.find(".query-parameters tr:gt(0)").each(function(){var row=$(this);var key=row.find("td:first").text();var checkbox=row.find('td:last input[type="checkbox"]')[0];var input=row.find("td:last input.form-control");checkbox.checked=dict[method_str][key].checkbox||false;input.val(dict[method_str][key].value||"")})})})}restore_route_parameters();restore_query_parameters();$.each(update_test_route_functions,function(){this()});$.each(update_test_query_functions,function(){this()})})})()}+"use strict";$.cookie.json=true;$.cookie.defaults.path=window.location.pathname;$.cookie.defaults.expires=365;(function(){function get_panel_ids_common(selector){var ids=[];$(selector).each(function(){ids.push($(this).attr("id"))});return ids}function get_opened_panel_ids(){return get_panel_ids_common(".collapse.in")}function get_all_panel_ids(){return get_panel_ids_common(".collapse")}function open_panel_by_id(id){$('[id="'+id+'"]').addClass("in")}function save_panel_state(){$.cookie("panel",get_opened_panel_ids())}function restore_panel_state(){$.each($.cookie("panel")||[],function(){open_panel_by_id(this)})}function close_all_panels(){$(".collapse.in").collapse("hide");save_panel_state([])}function open_all_panels(){$(".collapse").not(".in").collapse("show");save_panel_state(get_all_panel_ids())}function toggle_all_panels(){get_opened_panel_ids().length==0?open_all_panels():close_all_panels()}$(function(){restore_panel_state();$(".collapse").on("shown.bs.collapse",save_panel_state);$(".collapse").on("hidden.bs.collapse",save_panel_state);$("h1").click(toggle_all_panels)})})();if(location.protocol.slice(0,4)=="http"){(function(){$(function(){var update_test_route_functions=[];var update_test_query_functions=[];$(".panel").each(function(){var panel=$(this);var panel_id=panel.find(".panel-collapse").attr("id");panel.find(".route-parameters tr:first").append($("<th>value</th>"));panel.find(".route-parameters tr:gt(0)").append($('<td><input class="form-control input-sm" /></td>'));panel.find(".method").each(function(){var method=$(this);var h4=method.find("h4:first");var method_str=h4.text();h4.remove();method.find(".action").each(function(){var action=$(this);action.detach();var action_wrapper=$('<div class="action-wrapper">');method.append(action_wrapper);var div=$('<div class="request-test clearfix">');div.append($('<button class="test-method btn btn-default">'+method_str+"</button>"));var span=$('<span class="route-string">');div.append(span);span.append($('<span class="route-url"></span>'));span.append($('<span class="route-params"></span>'));action_wrapper.append(div);action_wrapper.append(action);var callout=$('<div class="hidden request-result bs-callout"><div class="clearfix title"><h4 class="pull-left"></h4></div><div class="well headers"><pre><code></code></pre></div><div class="body"><pre><code></code></pre></div></div>');var close=$('<div class="close-btn pull-right">×</div>');callout.find(".title").append(close);close.click(function(){callout.addClass("hidden")});method.find(".action:last").append(callout);action.find(".query-parameters tr:first").append($("<th>value</th>"));action.find(".query-parameters tr:gt(0)").append($('<td><div class="input-group"><span class="input-group-addon"><input type="checkbox"/></span><input class="form-control input-sm"/></div></td>'))})});var route_params={};panel.find(".route-parameters tr:gt(0)").each(function(){var p=$(this);route_params[p.find("td:first").text()]=p.find("td:last input")});function get_route(title){var ret="";title.children().each(function(){var p=$(this);if(p.hasClass("splitter")){ret+="/"}else if(p.hasClass("path")){ret+=p.text()}else if(p.hasClass("fetch")){ret+=route_params[p.text()].val()}});return ret}var route_template=panel.find(".panel-title");var route_url=panel.find(".route-url");function update_test_route(){var route=get_route(route_template);route_url.text(route)}update_test_route_functions.push(update_test_route);function save_route_parameters(){var save={};$.each(route_params,function(k,v){save[k]=v.val()});var rp=$.cookie("route")||{};rp[panel_id]=save;$.cookie("route",rp)}panel.find(".route-parameters tr:gt(0) input").keyup(update_test_route).change(save_route_parameters);panel.find(".method").each(function(){var method=$(this);var method_str=method.find(".test-method:first").text();var action_wrapper=method.find(".action-wrapper");var action_length=action_wrapper.length;action_wrapper.each(function(action_index){var action=$(this);var query_params={};action.find(".query-parameters tr:gt(0)").each(function(){var p=$(this);var checkbox=p.find('td:last input[type="checkbox"]');var input=p.find("td:last input.form-control");query_params[p.find("td:first").text()]={checkbox:checkbox[0],input:input}});function get_query_param(){var ret="";$.each(query_params,function(name,value){if(value.checkbox.checked){var val=value.input.val();if(val.length==0){ret+="&"+name}else{$.each(val.split(" "),function(){ret+="&"+name+"="+this})}}});if(ret.length>0){ret="?"+ret.slice(1)}return ret}var query_param=action.find(".route-params");function update_query_param(){query_param.text(get_query_param())}update_test_query_functions.push(update_query_param);function save_query_param(){var save={};$.each(query_params,function(name,value){save[name]={checkbox:value.checkbox.checked,value:value.input.val()}});var qp=$.cookie("query")||{};if(!qp[panel_id]){qp[panel_id]={}}if(!qp[panel_id][method_str]){qp[panel_id][method_str]=new Array(action_length)}qp[panel_id][method_str][action_index]=save;$.cookie("query",qp)}function update_and_save_query_param(){update_query_param();save_query_param()}action.find('.query-parameters input[type="checkbox"]').change(update_and_save_query_param);action.find(".query-parameters input.form-control").keyup(update_query_param).change(save_query_param);var action_btn=action.find(".test-method");var route_url=action.find(".route-url");var result=action.find(".request-result");var accept=action.find(".precondition-accept span");function request_test(){if(action_btn.hasClass("disabled")){return}var url=route_url.text()+query_param.text();var meth=method_str=="*"?"GET":method_str;action_btn.addClass("disabled");function request_callback(data,status,xhr){var ct=xhr.getResponseHeader("Content-Type");var code=xhr.status;result.find("h4").text(code+" "+status+' "'+ct+'"');result.find(".headers code").text(xhr.getAllResponseHeaders());if(ct.slice(0,16)=="application/json"){try{var json=JSON.stringify(JSON.parse(data),undefined,2);result.find(".body code").text(json)}catch(e){result.find(".body code").text(e+"\n\n"+data)}}else{result.find(".body code").text(data)}}function beforeSend(xhr){if(accept.length!=0){xhr.setRequestHeader("Accept",accept.text())}}$.ajax({type:meth,url:url,dataType:"text",beforeSend:beforeSend}).done(function(data,status,xhr){request_callback(data,status,xhr);result.addClass("bs-callout-primary");result.removeClass("bs-callout-danger")}).fail(function(xhr,status,err){request_callback(err,status,xhr);result.addClass("bs-callout-danger");result.removeClass("bs-callout-primary")}).always(function(){result.removeClass("hidden");action_btn.removeClass("disabled")})}action.find(".test-method").click(request_test);action.find("input.form-control").keypress(function(e){if(e.which==13){request_test()}})})})});function restore_route_parameters(){$.each($.cookie("route")||{},function(id,dict){var panel=$('[id="'+id+'"]');panel.find(".route-parameters tr:gt(0)").each(function(){var row=$(this);var key=row.find("td:first").text();row.find("td:last input").val(dict[key]||"")})})}function restore_query_parameters(){try{$.each($.cookie("query")||{},function(id,dict){var panel=$('[id="'+id+'"]');panel.find(".method").each(function(){var method=$(this);var method_str=method.find(".test-method:first").text();var action=method.find(".action-wrapper");if(action.length!=dict[method_str].length){throw"action length mismatch"}action.each(function(idx){$(this).find(".query-parameters tr:gt(0)").each(function(){var row=$(this);var key=row.find("td:first").text();var checkbox=row.find('td:last input[type="checkbox"]')[0];var input=row.find("td:last input.form-control");if(dict[method_str][idx]){checkbox.checked=dict[method_str][idx][key].checkbox||false;input.val(dict[method_str][idx][key].value||"")}})})})})}catch(e){console.log(e);$.removeCookie("query")}}restore_route_parameters();restore_query_parameters();$.each(update_test_route_functions,function(){this()});$.each(update_test_query_functions,function(){this()})})})()}