From c05371b46913cc20cc1d24afd8b71acff61b322d Mon Sep 17 00:00:00 2001 From: Abhinav Sarkar Date: Mon, 28 Aug 2017 11:14:37 +0530 Subject: [PATCH] Types + Persistence --- .gitignore | 8 ++ bower.json | 19 +++++ package-lock.json | 121 +++++++++++++++++++++++++++++ package.json | 18 +++++ src/Main.purs | 9 +++ src/SimpleService/Persistence.purs | 38 +++++++++ src/SimpleService/Types.purs | 39 ++++++++++ test/Main.purs | 9 +++ 8 files changed, 261 insertions(+) create mode 100644 .gitignore create mode 100644 bower.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/Main.purs create mode 100644 src/SimpleService/Persistence.purs create mode 100644 src/SimpleService/Types.purs create mode 100644 test/Main.purs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9623fa5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/bower_components/ +/node_modules/ +/.pulp-cache/ +/output/ +/generated-docs/ +/.psc* +/.purs* +/.psa* diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..08bafbc --- /dev/null +++ b/bower.json @@ -0,0 +1,19 @@ +{ + "name": "ps-simple-rest-service", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "output" + ], + "dependencies": { + "purescript-prelude": "^3.1.0", + "purescript-console": "^3.0.0", + "purescript-foreign-generic": "^5.0.0", + "purescript-postgresql-client": "^2.1.0", + "purescript-aff": "^3.1.0" + }, + "devDependencies": { + "purescript-psci-support": "^3.0.0" + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e77465e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,121 @@ +{ + "name": "ps-simple-rest-service", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "buffer-writer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.1.tgz", + "integrity": "sha1-Iqk2kB4wKa/NdUfrRIfOtpejvwg=" + }, + "generic-pool": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.4.3.tgz", + "integrity": "sha1-eAw29p360FpaBF3Te+etyhGk9v8=" + }, + "object-assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=" + }, + "packet-reader": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz", + "integrity": "sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc=" + }, + "pg": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-6.4.0.tgz", + "integrity": "sha1-y3a6Lnwuq4n8ZL96n+ZIztckNtw=", + "requires": { + "buffer-writer": "1.0.1", + "packet-reader": "0.3.1", + "pg-connection-string": "0.1.3", + "pg-pool": "1.8.0", + "pg-types": "1.12.1", + "pgpass": "1.0.2", + "semver": "4.3.2" + } + }, + "pg-connection-string": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz", + "integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=" + }, + "pg-pool": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-1.8.0.tgz", + "integrity": "sha1-9+xzgkw3oD8Hb1G/33DjQBR8Tzc=", + "requires": { + "generic-pool": "2.4.3", + "object-assign": "4.1.0" + } + }, + "pg-types": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.12.1.tgz", + "integrity": "sha1-1kCH45A7WP+q0nnnWVxSIIoUw9I=", + "requires": { + "postgres-array": "1.0.2", + "postgres-bytea": "1.0.0", + "postgres-date": "1.0.3", + "postgres-interval": "1.1.1" + } + }, + "pgpass": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz", + "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=", + "requires": { + "split": "1.0.1" + } + }, + "postgres-array": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.2.tgz", + "integrity": "sha1-jgsy6wO/d6XAp4UeBEHBaaJWojg=" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" + }, + "postgres-date": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.3.tgz", + "integrity": "sha1-4tiXAu/bJY/52c7g/pG9BpdSV6g=" + }, + "postgres-interval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.1.1.tgz", + "integrity": "sha512-OkuCi9t/3CZmeQreutGgx/OVNv9MKHGIT5jH8KldQ4NLYXkvmT9nDVxEuCENlNwhlGPE374oA/xMqn05G49pHA==", + "requires": { + "xtend": "4.0.1" + } + }, + "semver": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", + "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "requires": { + "through": "2.3.8" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..65b698a --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "ps-simple-rest-service", + "version": "1.0.0", + "description": "", + "main": "index.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "pg": "^6.4.0" + } +} diff --git a/src/Main.purs b/src/Main.purs new file mode 100644 index 0000000..abe68ec --- /dev/null +++ b/src/Main.purs @@ -0,0 +1,9 @@ +module Main where + +import Prelude +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Console (CONSOLE, log) + +main :: forall e. Eff (console :: CONSOLE | e) Unit +main = do + log "Hello sailor!" diff --git a/src/SimpleService/Persistence.purs b/src/SimpleService/Persistence.purs new file mode 100644 index 0000000..5ae0497 --- /dev/null +++ b/src/SimpleService/Persistence.purs @@ -0,0 +1,38 @@ +module SimpleServer.Persistence + ( insertUser + , findUser + , updateUser + , deleteUser + ) where + +import Prelude + +import Control.Monad.Aff (Aff) +import Data.Array as Array +import Data.Maybe (Maybe) +import Database.PostgreSQL as PG +import SimpleServer.Types (User(..), UserID) + +insertUserQuery :: String +insertUserQuery = "insert into users (id, name) values ($1, $2)" + +findUserQuery :: String +findUserQuery = "select id, name from users where id = $1" + +updateUserQuery :: String +updateUserQuery = "update users set name = $1 where id = $2" + +deleteUserQuery :: String +deleteUserQuery = "delete from users where id = $1" + +insertUser :: forall eff. PG.Connection -> User -> Aff (postgreSQL :: PG.POSTGRESQL | eff) Unit +insertUser conn user = PG.execute conn (PG.Query insertUserQuery) user + +findUser :: forall eff. PG.Connection -> UserID -> Aff (postgreSQL :: PG.POSTGRESQL | eff) (Maybe User) +findUser conn userID = map Array.head $ PG.query conn (PG.Query findUserQuery) (PG.Row1 userID) + +updateUser :: forall eff. PG.Connection -> User -> Aff (postgreSQL :: PG.POSTGRESQL | eff) Unit +updateUser conn (User {id, name}) = PG.execute conn (PG.Query updateUserQuery) (PG.Row2 name id) + +deleteUser :: forall eff. PG.Connection -> UserID -> Aff (postgreSQL :: PG.POSTGRESQL | eff) Unit +deleteUser conn userID = PG.execute conn (PG.Query deleteUserQuery) (PG.Row1 userID) diff --git a/src/SimpleService/Types.purs b/src/SimpleService/Types.purs new file mode 100644 index 0000000..d4643be --- /dev/null +++ b/src/SimpleService/Types.purs @@ -0,0 +1,39 @@ +module SimpleServer.Types where + +import Prelude + +import Data.Array as Array +import Data.Either (Either(..)) +import Data.Foreign.Class (class Decode, class Encode) +import Data.Foreign.Generic (defaultOptions, genericDecode, genericEncode) +import Data.Generic.Rep (class Generic) +import Data.Generic.Rep.Show (genericShow) +import Database.PostgreSQL (class FromSQLRow, class ToSQLRow, fromSQLValue, toSQLValue) + +type UserID = Int + +newtype User = User + { id :: UserID + , name :: String + } + +derive instance genericUser :: Generic User _ + +instance showUser :: Show User where + show = genericShow + +instance decodeUser :: Decode User where + decode = genericDecode $ defaultOptions { unwrapSingleConstructors = true } + +instance encodeUser :: Encode User where + encode = genericEncode $ defaultOptions { unwrapSingleConstructors = true } + +instance userFromSQLRow :: FromSQLRow User where + fromSQLRow [id, name] = + User <$> ({ id: _, name: _} <$> fromSQLValue id <*> fromSQLValue name) + + fromSQLRow xs = Left $ "Row has " <> show n <> " fields, expecting 2." + where n = Array.length xs + +instance userToSQLRow :: ToSQLRow User where + toSQLRow (User {id, name}) = [toSQLValue id, toSQLValue name] diff --git a/test/Main.purs b/test/Main.purs new file mode 100644 index 0000000..845d0f4 --- /dev/null +++ b/test/Main.purs @@ -0,0 +1,9 @@ +module Test.Main where + +import Prelude +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Console (CONSOLE, log) + +main :: forall e. Eff (console :: CONSOLE | e) Unit +main = do + log "You should add some tests."