Skip to content

Instantly share code, notes, and snippets.

@PhilOwen
Created January 3, 2017 03:59
Show Gist options
  • Save PhilOwen/17f4085e68fef8409379d701a0a81f06 to your computer and use it in GitHub Desktop.
Save PhilOwen/17f4085e68fef8409379d701a0a81f06 to your computer and use it in GitHub Desktop.
HaskellのServantとPersistentでデータの簡易配信

HaskellのウェブフレームワークらしきServantで、 SQLiteのデータを配信できるようにする。

「ウェブフレームワークらしき」というのは、 JSONでやりとりするマイクロサービスを作るのはカンタンだが、 HTMLを返すのに一手間かかるという、 ウェブフレームワークらしからぬ仕様なので。 あと、Servantはウェブサーバだけでなく、 クライアント側の開発もサポートしている。 フルスタックではないし、これからもどこか不思議な方向を目指している感じ。
Haskell界隈で微妙に名前を聞いて、 シンプルで快速らしいのでやってみた。

ApplicationServerProxyなど、 普通とは違った意味で使われていて、最初戸惑う。 また、言語拡張されて、型の考え方もかなり特殊。 とはいえ、まずAPIを定義(ルーティングを定義)して、 後でそれを実装するようなServerを書くと考えれば、 一応一貫性があってわかりやすいかもしれない。
ただ、importや言語拡張の山で、 何も見ずに始めるのはほぼムリ (今回Persistentと合わせたので、余計ひどい)。 テンプレートから始めて、書き換えていくのが自然。

References

import Data.Time
import Data.Aeson
import Control.Monad.Logger
import Control.Monad.IO.Class
import Control.Monad.Trans.Resource (runResourceT)
import GHC.Generics
import Network.Wai
import Network.Wai.Handler.Warp
import Servant
import Database.Persist
import Database.Persist.Quasi
import Database.Persist.TH
import Database.Persist.Sqlite
share [mkPersist sqlSettings, mkMigrate "migrateAll"]
$(persistFileWith lowerCaseSettings "models")
instance ToJSON SensorData
-- routing
type CombinedAPI = HomeAPI
:<|> "data" :> DataAPI
type HomeAPI = Get '[JSON] ()
type DataAPI = Get '[JSON] [SensorData]
main :: IO ()
main = runStderrLoggingT $
withSqlitePool "log.sqlite" 5 $ \pool -> liftIO $ do
doMigrate pool
run 8080 (app1 pool)
app1 :: ConnectionPool -> Application
app1 pool = serve api $ combinedServer pool
api :: Proxy CombinedAPI
api = Proxy
-- Server **APIs
combinedServer :: ConnectionPool -> Server CombinedAPI
combinedServer pool = homeServer
:<|> dataServer pool
homeServer :: Server HomeAPI
homeServer = throwError err302'
where err302' = err302 {errHeaders = [("Location", "http://localhost:8080/data")]}
dataServer :: ConnectionPool -> Server DataAPI
dataServer pool = do
let select = selectList [] [Desc SensorDataCreated, LimitTo 50]
items <- runSqlPool select pool
return [value | Entity _ value <- items]
-- misc
doMigrate :: ConnectionPool -> IO ()
doMigrate pool = runResourceT $
runSqlPool (runMigration migrateAll) pool
SensorData
created UTCTime
val1 Int
val2 Int
val3 Int
val4 Int
deriving Show Read Eq Ord Generic
name: persistent-data
version: 0.1
category: Web
build-type: Simple
cabal-version: >= 1.20
executable persistent-data-server
main-is: Main.hs
ghc-options: -Wall
default-language: Haskell2010
build-depends:
base
, aeson
, containers
, servant-server
, wai
, warp
, time
, monad-logger
, resourcet
, persistent
, persistent-template
, persistent-sqlite
default-extensions:
OverloadedStrings
, GADTs
, GeneralizedNewtypeDeriving
, MultiParamTypeClasses
, QuasiQuotes
, TemplateHaskell
, TypeFamilies
, DataKinds
, DeriveGeneric
, TypeOperators
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE "sensor_data"("id" INTEGER PRIMARY KEY,"created" TIMESTAMP NOT NULL,"val1" INTEGER NOT NULL,"val2" INTEGER NOT NULL,"val3" INTEGER NOT NULL,"val4" INTEGER NOT NULL);
INSERT INTO "sensor_data" VALUES(1,'2017-01-03T02:30:02.457641',226,1,18,241);
INSERT INTO "sensor_data" VALUES(2,'2017-01-03T02:30:02.574624',111,214,142,110);
INSERT INTO "sensor_data" VALUES(3,'2017-01-03T02:30:02.677518',97,195,116,65);
INSERT INTO "sensor_data" VALUES(4,'2017-01-03T02:30:02.778752',47,32,78,79);
INSERT INTO "sensor_data" VALUES(5,'2017-01-03T02:30:02.889444',65,177,89,128);
INSERT INTO "sensor_data" VALUES(6,'2017-01-03T02:30:02.994967',159,17,129,227);
INSERT INTO "sensor_data" VALUES(7,'2017-01-03T02:30:03.097499',143,252,162,134);
INSERT INTO "sensor_data" VALUES(8,'2017-01-03T02:30:03.202987',102,67,215,59);
INSERT INTO "sensor_data" VALUES(9,'2017-01-03T02:30:03.307562',89,240,183,241);
INSERT INTO "sensor_data" VALUES(10,'2017-01-03T02:30:03.41304',16,38,169,150);
INSERT INTO "sensor_data" VALUES(11,'2017-01-03T02:30:03.517555',70,127,18,29);
INSERT INTO "sensor_data" VALUES(12,'2017-01-03T02:30:03.623054',169,118,37,225);
INSERT INTO "sensor_data" VALUES(13,'2017-01-03T02:30:03.743016',164,21,65,201);
INSERT INTO "sensor_data" VALUES(14,'2017-01-03T02:30:03.847569',21,59,117,101);
INSERT INTO "sensor_data" VALUES(15,'2017-01-03T02:30:03.953031',235,212,67,9);
INSERT INTO "sensor_data" VALUES(16,'2017-01-03T02:30:04.057545',246,237,177,144);
INSERT INTO "sensor_data" VALUES(17,'2017-01-03T02:30:04.163005',170,149,171,239);
INSERT INTO "sensor_data" VALUES(18,'2017-01-03T02:30:04.267547',45,107,221,229);
INSERT INTO "sensor_data" VALUES(19,'2017-01-03T02:30:04.373084',40,111,42,207);
INSERT INTO "sensor_data" VALUES(20,'2017-01-03T02:30:04.477527',33,90,16,157);
INSERT INTO "sensor_data" VALUES(21,'2017-01-03T02:30:04.583116',67,94,185,22);
INSERT INTO "sensor_data" VALUES(22,'2017-01-03T02:30:04.687612',155,33,43,207);
INSERT INTO "sensor_data" VALUES(23,'2017-01-03T02:30:04.788784',143,77,243,116);
INSERT INTO "sensor_data" VALUES(24,'2017-01-03T02:30:04.890848',231,27,155,243);
INSERT INTO "sensor_data" VALUES(25,'2017-01-03T02:30:04.996366',86,77,26,64);
INSERT INTO "sensor_data" VALUES(26,'2017-01-03T02:30:05.097581',12,160,62,26);
INSERT INTO "sensor_data" VALUES(27,'2017-01-03T02:30:05.203052',210,150,237,164);
INSERT INTO "sensor_data" VALUES(28,'2017-01-03T02:30:05.30758',215,47,152,210);
INSERT INTO "sensor_data" VALUES(29,'2017-01-03T02:30:05.413122',3,28,156,97);
INSERT INTO "sensor_data" VALUES(30,'2017-01-03T02:30:05.517673',206,233,123,173);
INSERT INTO "sensor_data" VALUES(31,'2017-01-03T02:30:05.62314',59,158,158,28);
INSERT INTO "sensor_data" VALUES(32,'2017-01-03T02:30:05.72761',77,236,29,166);
INSERT INTO "sensor_data" VALUES(33,'2017-01-03T02:30:05.833085',176,17,67,152);
INSERT INTO "sensor_data" VALUES(34,'2017-01-03T02:30:05.937641',248,174,157,231);
INSERT INTO "sensor_data" VALUES(35,'2017-01-03T02:30:06.043115',7,206,52,109);
INSERT INTO "sensor_data" VALUES(36,'2017-01-03T02:30:06.147595',83,121,124,98);
INSERT INTO "sensor_data" VALUES(37,'2017-01-03T02:30:06.253078',254,95,142,229);
INSERT INTO "sensor_data" VALUES(38,'2017-01-03T02:30:06.357602',116,94,70,170);
INSERT INTO "sensor_data" VALUES(39,'2017-01-03T02:30:06.463102',170,62,100,82);
INSERT INTO "sensor_data" VALUES(40,'2017-01-03T02:30:06.56773',56,126,175,127);
INSERT INTO "sensor_data" VALUES(41,'2017-01-03T02:30:06.668793',151,152,133,113);
INSERT INTO "sensor_data" VALUES(42,'2017-01-03T02:30:06.774283',147,56,137,212);
INSERT INTO "sensor_data" VALUES(43,'2017-01-03T02:30:06.874662',58,20,84,106);
INSERT INTO "sensor_data" VALUES(44,'2017-01-03T02:30:06.977668',155,16,234,243);
INSERT INTO "sensor_data" VALUES(45,'2017-01-03T02:30:07.078944',228,197,200,119);
INSERT INTO "sensor_data" VALUES(46,'2017-01-03T02:30:07.18442',103,70,199,209);
INSERT INTO "sensor_data" VALUES(47,'2017-01-03T02:30:07.287652',92,55,118,61);
INSERT INTO "sensor_data" VALUES(48,'2017-01-03T02:30:07.388988',53,119,186,181);
INSERT INTO "sensor_data" VALUES(49,'2017-01-03T02:30:07.494522',196,88,138,111);
INSERT INTO "sensor_data" VALUES(50,'2017-01-03T02:30:07.597694',23,75,240,190);
INSERT INTO "sensor_data" VALUES(51,'2017-01-03T02:30:07.708408',61,238,194,250);
INSERT INTO "sensor_data" VALUES(52,'2017-01-03T02:30:07.813902',196,8,62,140);
INSERT INTO "sensor_data" VALUES(53,'2017-01-03T02:30:07.917687',10,133,82,85);
INSERT INTO "sensor_data" VALUES(54,'2017-01-03T02:30:08.023168',4,117,79,42);
INSERT INTO "sensor_data" VALUES(55,'2017-01-03T02:30:08.127706',61,56,200,131);
INSERT INTO "sensor_data" VALUES(56,'2017-01-03T02:30:08.233189',92,34,229,206);
INSERT INTO "sensor_data" VALUES(57,'2017-01-03T02:30:08.337706',252,254,196,106);
INSERT INTO "sensor_data" VALUES(58,'2017-01-03T02:30:08.438824',65,93,206,175);
INSERT INTO "sensor_data" VALUES(59,'2017-01-03T02:30:08.544444',244,223,145,22);
INSERT INTO "sensor_data" VALUES(60,'2017-01-03T02:30:08.647798',70,75,71,209);
INSERT INTO "sensor_data" VALUES(61,'2017-01-03T02:30:08.753279',206,250,55,155);
INSERT INTO "sensor_data" VALUES(62,'2017-01-03T02:30:08.857783',101,62,125,205);
INSERT INTO "sensor_data" VALUES(63,'2017-01-03T02:30:08.96326',249,1,239,96);
INSERT INTO "sensor_data" VALUES(64,'2017-01-03T02:30:09.067798',244,116,127,29);
INSERT INTO "sensor_data" VALUES(65,'2017-01-03T02:30:09.173252',56,187,45,114);
INSERT INTO "sensor_data" VALUES(66,'2017-01-03T02:30:09.277765',60,51,112,17);
INSERT INTO "sensor_data" VALUES(67,'2017-01-03T02:30:09.383229',130,153,187,170);
INSERT INTO "sensor_data" VALUES(68,'2017-01-03T02:30:09.487748',222,170,107,88);
INSERT INTO "sensor_data" VALUES(69,'2017-01-03T02:30:09.59327',73,150,23,17);
INSERT INTO "sensor_data" VALUES(70,'2017-01-03T02:30:09.697762',203,196,128,11);
INSERT INTO "sensor_data" VALUES(71,'2017-01-03T02:30:09.803245',83,215,154,66);
INSERT INTO "sensor_data" VALUES(72,'2017-01-03T02:30:09.905638',7,195,232,155);
INSERT INTO "sensor_data" VALUES(73,'2017-01-03T02:30:10.007774',117,245,235,197);
INSERT INTO "sensor_data" VALUES(74,'2017-01-03T02:30:10.11333',108,107,37,144);
INSERT INTO "sensor_data" VALUES(75,'2017-01-03T02:30:10.217787',23,216,141,161);
INSERT INTO "sensor_data" VALUES(76,'2017-01-03T02:30:10.318941',165,248,164,208);
INSERT INTO "sensor_data" VALUES(77,'2017-01-03T02:30:10.424454',134,17,117,149);
INSERT INTO "sensor_data" VALUES(78,'2017-01-03T02:30:10.527784',154,19,43,245);
INSERT INTO "sensor_data" VALUES(79,'2017-01-03T02:30:10.633306',208,148,165,221);
INSERT INTO "sensor_data" VALUES(80,'2017-01-03T02:30:10.737805',102,16,120,0);
INSERT INTO "sensor_data" VALUES(81,'2017-01-03T02:30:10.839962',162,18,106,117);
INSERT INTO "sensor_data" VALUES(82,'2017-01-03T02:30:10.945544',139,75,109,111);
INSERT INTO "sensor_data" VALUES(83,'2017-01-03T02:30:11.047848',74,76,23,53);
INSERT INTO "sensor_data" VALUES(84,'2017-01-03T02:30:11.15331',199,213,133,6);
INSERT INTO "sensor_data" VALUES(85,'2017-01-03T02:30:11.257807',106,243,197,83);
INSERT INTO "sensor_data" VALUES(86,'2017-01-03T02:30:11.36329',112,15,48,178);
INSERT INTO "sensor_data" VALUES(87,'2017-01-03T02:30:11.467819',220,81,179,76);
INSERT INTO "sensor_data" VALUES(88,'2017-01-03T02:30:11.573304',178,231,130,33);
INSERT INTO "sensor_data" VALUES(89,'2017-01-03T02:30:11.677855',20,241,142,28);
INSERT INTO "sensor_data" VALUES(90,'2017-01-03T02:30:11.783354',55,234,204,173);
INSERT INTO "sensor_data" VALUES(91,'2017-01-03T02:30:11.887872',164,2,10,77);
INSERT INTO "sensor_data" VALUES(92,'2017-01-03T02:30:11.993339',223,186,103,36);
INSERT INTO "sensor_data" VALUES(93,'2017-01-03T02:30:12.09789',14,194,246,137);
INSERT INTO "sensor_data" VALUES(94,'2017-01-03T02:30:12.198987',245,200,104,70);
INSERT INTO "sensor_data" VALUES(95,'2017-01-03T02:30:12.304473',33,68,92,90);
INSERT INTO "sensor_data" VALUES(96,'2017-01-03T02:30:12.405712',180,134,26,224);
INSERT INTO "sensor_data" VALUES(97,'2017-01-03T02:30:12.507851',45,28,168,142);
INSERT INTO "sensor_data" VALUES(98,'2017-01-03T02:30:12.613334',136,178,252,97);
INSERT INTO "sensor_data" VALUES(99,'2017-01-03T02:30:12.717894',106,120,107,79);
INSERT INTO "sensor_data" VALUES(100,'2017-01-03T02:30:12.823357',44,239,35,195);
COMMIT;
resolver: lts-7.14
packages:
- '.'
extra-deps: []
flags: {}
extra-package-dbs: []
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment