avers-api (empty) → 0.0.1
raw patch · 7 files changed
+523/−0 lines, 7 filesdep +aesondep +aversdep +basesetup-changed
Dependencies added: aeson, avers, base, bytestring, cookie, servant, text, time
Files
- LICENSE +21/−0
- Setup.hs +2/−0
- avers-api.cabal +40/−0
- src/Avers/API.hs +166/−0
- src/Avers/API/Credentials.hs +17/−0
- src/Avers/API/Instances.hs +15/−0
- src/Avers/API/Types.hs +262/−0
+ LICENSE view
@@ -0,0 +1,21 @@+The MIT License (MIT)++Copyright (c) 2016 Tomas Carnecky++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ avers-api.cabal view
@@ -0,0 +1,40 @@+name: avers-api+version: 0.0.1+synopsis: Types describing the core and extended Avers APIs+description: See README+homepage: http://github.com/wereHamster/avers-api+license: MIT+license-file: LICENSE+author: Tomas Carnecky+maintainer: tomas.carnecky@gmail.com+copyright: 2016 Tomas Carnecky+category: Avers+build-type: Simple+cabal-version: >=1.10++source-repository head+ type: git+ location: https://github.com/wereHamster/avers-api++library+ default-language: Haskell2010+ hs-source-dirs: src+ ghc-options: -Wall++ exposed-modules:+ Avers.API++ other-modules:+ Avers.API.Types+ , Avers.API.Instances+ , Avers.API.Credentials++ build-depends:+ base >= 4.7 && < 5+ , aeson+ , avers+ , bytestring+ , cookie+ , servant+ , text+ , time
+ src/Avers/API.hs view
@@ -0,0 +1,166 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++module Avers.API+ ( AversCoreAPI, AversSessionAPI, AversBlobAPI, AversAccountAPI++ , module Avers.API.Types+ , module Avers.API.Credentials+ ) where+++import Data.Text (Text)+import Data.ByteString (ByteString)++import Avers.Types (ObjId, RevId, BlobId, SessionId)++import Avers.API.Types+import Avers.API.Instances ()+import Avers.API.Credentials++import Servant.API++import Web.Cookie++++--------------------------------------------------------------------------------+-- | The Core API to manipulate objects, patches, releases etc.++type AversCoreAPI++ ------------------------------------------------------------------------------+ -- CreateObject+ = "objects"+ :> Credentials+ :> ReqBody '[JSON] CreateObjectBody+ :> Post '[JSON] CreateObjectResponse++ ------------------------------------------------------------------------------+ -- LookupObject+ :<|> "objects" :> Capture "objId" ObjId+ :> Credentials+ :> Get '[JSON] LookupObjectResponse++ ------------------------------------------------------------------------------+ -- PatchObject+ :<|> "objects" :> Capture "objId" ObjId+ :> Credentials+ :> ReqBody '[JSON] PatchObjectBody+ :> Patch '[JSON] PatchObjectResponse++ ------------------------------------------------------------------------------+ -- DeleteObject+ :<|> "objects" :> Capture "objId" ObjId+ :> Credentials+ :> Delete '[JSON] ()++ ------------------------------------------------------------------------------+ -- LookupPatch+ :<|> "objects" :> Capture "objId" ObjId :> Capture "revId" RevId+ :> Credentials+ :> Get '[JSON] LookupPatchResponse++ ------------------------------------------------------------------------------+ -- ObjectChanges+ :<|> "objects" :> Capture "objId" ObjId :> "changes"+ :> Credentials+ :> Raw -- WebSocket, stream of ObjectChangeNotification++ ------------------------------------------------------------------------------+ -- CreateRelease+ :<|> "objects" :> Capture "objId" ObjId :> "releases"+ :> Credentials+ :> ReqBody '[JSON] CreateReleaseBody+ :> Post '[JSON] CreateReleaseResponse++ ------------------------------------------------------------------------------+ -- LookupRelease+ :<|> "objects" :> Capture "objId" ObjId :> "releases" :> Capture "revId" RevId+ :> Credentials+ :> Get '[JSON] LookupReleaseResponse++ ------------------------------------------------------------------------------+ -- LookupLatestRelease+ :<|> "objects" :> Capture "objId" ObjId :> "releases" :> "_latest"+ :> Credentials+ :> Get '[JSON] LookupLatestReleaseResponse++ ------------------------------------------------------------------------------+ -- ChangeSecret+ :<|> "secret"+ :> Credentials+ :> ReqBody '[JSON] ChangeSecretBody+ :> Post '[] ()++++--------------------------------------------------------------------------------+-- | API to create and maintain sessions. Also contains API to+-- change the secret, which is part of the session code.++type AversSessionAPI++ ------------------------------------------------------------------------------+ -- CreateSession+ = "session"+ :> ReqBody '[JSON] CreateSessionBody+ :> Post '[JSON] (Headers '[Header "Set-Cookie" SetCookie] CreateSessionResponse)++ ------------------------------------------------------------------------------+ -- LookupSession+ :<|> "session"+ :> SessionId+ :> Get '[JSON] (Headers '[Header "Set-Cookie" SetCookie] LookupSessionResponse)++ ------------------------------------------------------------------------------+ -- DeleteSession+ :<|> "session"+ :> SessionId+ :> Delete '[JSON] (Headers '[Header "Set-Cookie" SetCookie] ())++++--------------------------------------------------------------------------------+-- | API to manange blobs++type AversBlobAPI++ ------------------------------------------------------------------------------+ -- UploadBlob+ = "blobs"+ :> Header "Content-Type" Text+ :> ReqBody '[OctetStream] ByteString+ :> Post '[JSON] UploadBlobResponse++ ------------------------------------------------------------------------------+ -- LookupBlob+ :<|> "blobs" :> Capture "blobId" BlobId+ :> Get '[JSON] LookupBlobResponse++ ------------------------------------------------------------------------------+ -- BlobContent+ :<|> "blobs" :> Capture "blobId" BlobId :> "content"+ :> Get '[OctetStream] (Headers '[Header "Content-Type" Text] ByteString)++++--------------------------------------+-- | API to manage accounts. These are optional and not really part of+-- Avers, but many applications do have a concept of a user or account,+-- some person which is using the API.++type AversAccountAPI++ ------------------------------------------------------------------------------+ -- Signup.+ --+ -- Similar to CreateObject but no authorization is required. Instead,+ -- a different mechanism is used to ensure that the client is allowed+ -- to execute the action (eg. an captcha).++ = "signup"+ :> ReqBody '[JSON] SignupBody+ :> Post '[JSON] SignupResponse
+ src/Avers/API/Credentials.hs view
@@ -0,0 +1,17 @@+module Avers.API.Credentials where+++import Avers++++-- | Credentials are used to authenticate the client. It can be a SessionId+-- (extracted from the 'session' cookie).+--+-- Later we may add support for token based authentication. Then we extend+-- this into a sumtype covering all the possible ways how credentials can be+-- passed along with the request.++data Credentials+ = SessionIdCredential !SessionId+ -- | AccessTokenCredential !AccessToken
+ src/Avers/API/Instances.hs view
@@ -0,0 +1,15 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Avers.API.Instances where+++import Avers+import Servant.API (FromText(..))++++instance FromText ObjId where+ fromText = Just . ObjId++instance FromText RevId where+ fromText x = fromText x >>= Just . RevId
+ src/Avers/API/Types.hs view
@@ -0,0 +1,262 @@+{-# LANGUAGE OverloadedStrings #-}++module Avers.API.Types where++import Data.Text (Text)+import Data.Aeson as A+import Data.Time+import Avers+++++--------------------------------------------------------------------------------+-- CreateObject++data CreateObjectBody = CreateObjectBody+ { cobType :: !Text+ , cobContent :: !Value+ }++instance FromJSON CreateObjectBody where+ parseJSON (A.Object o) = CreateObjectBody <$> o .: "type" <*> o .: "content"+ parseJSON _ = fail "CreateObjectBody"+++data CreateObjectResponse = CreateObjectResponse+ { corId :: !ObjId+ , corType :: !Text+ , corContent :: !Value+ }++instance ToJSON CreateObjectResponse where+ toJSON x = object+ [ "id" .= corId x+ , "type" .= corType x+ , "content" .= corContent x+ ]++++--------------------------------------------------------------------------------+-- LookupObject++data LookupObjectResponse = LookupObjectResponse+ { lorId :: !ObjId+ , lorType :: !Text+ , lorCreatedAt :: !UTCTime+ , lorCreatedBy :: !ObjId+ , lorRevisionId :: !RevId+ , lorContent :: !Value+ }++instance ToJSON LookupObjectResponse where+ toJSON x = object+ [ "id" .= lorId x+ , "type" .= lorType x+ , "createdAt" .= lorCreatedAt x+ , "createdBy" .= lorCreatedBy x+ , "revisionId" .= lorRevisionId x+ , "content" .= lorContent x+ ]++++--------------------------------------------------------------------------------+-- PatchObject++data PatchObjectBody = PatchObjectBody+ { pobRevisionId :: !RevId+ -- ^ The 'RevId' against which the client created the operations. This may+ -- be a bit behind if some other client submitted patches in parallel.++ , pobOperations :: ![Operation]+ -- ^ The operations which the client wants to store in the database.+ }++instance FromJSON PatchObjectBody where+ parseJSON (A.Object o) = PatchObjectBody <$> o .: "revisionId" <*> o .: "operations"+ parseJSON _ = fail "PatchObjectBody"+++data PatchObjectResponse = PatchObjectResponse+ { porPreviousPatches :: ![Patch]+ -- ^ Patches which were already in the database. The submitted ops were+ -- rebased on top of these.++ , porNumProcessedOperations :: !Int+ -- ^ The number of operations which were processed. This may be smaller+ -- than the number of submitted ops if the processing failed somewhere+ -- in the middle. The client can then decide what to do with those which+ -- were not accepted.++ , porResultingPatches :: ![Patch]+ -- ^ Out of the submitted operations, these are the patches which were+ -- actually applied and stored in the database. This list may be shorter+ -- if some operations were dropped (because redundant or conflicting).+ }++instance ToJSON PatchObjectResponse where+ toJSON x = object+ [ "previousPatches" .= porPreviousPatches x+ , "numProcessedOperations" .= porNumProcessedOperations x+ , "resultingPatches" .= porResultingPatches x+ ]++++--------------------------------------------------------------------------------+-- ObjectChanges++data ObjectChangeNotification+ = PatchNotification !Patch+ -- ^ A new patch was created.++ -- | LatestReleaseNotification !Release+ -- ^ A new release was created.++instance ToJSON ObjectChangeNotification where+ toJSON (PatchNotification p) = object [ "type" .= ("patch" :: Text), "content" .= p ]+ -- toJSON (ReleaseNotification r) = object [ "type" .= ("release" :: Text), "content" .= r ]++++--------------------------------------------------------------------------------+-- LookupPatch++type LookupPatchResponse = Patch++++--------------------------------------------------------------------------------+-- CreateRelease++data CreateReleaseBody = CreateReleaseBody+ {+ }++instance FromJSON CreateReleaseBody where+ parseJSON = undefined+++data CreateReleaseResponse = CreateReleaseResponse+ {+ }++instance ToJSON CreateReleaseResponse where+ toJSON = undefined++++--------------------------------------------------------------------------------+-- LookupRelease++data LookupReleaseResponse = LookupReleaseResponse+ {+ }++instance ToJSON LookupReleaseResponse where+ toJSON = undefined++++--------------------------------------------------------------------------------+-- LookupLatestRelease++data LookupLatestReleaseResponse = LookupLatestReleaseResponse+ {+ }++instance ToJSON LookupLatestReleaseResponse where+ toJSON = undefined++++--------------------------------------------------------------------------------+-- CreateSession++data CreateSessionBody = CreateSessionBody+ { csbLogin :: !SecretId+ , csbSecret :: !Text+ }++instance FromJSON CreateSessionBody where+ parseJSON (A.Object o) = CreateSessionBody <$> o .: "login" <*> o .: "secret"+ parseJSON _ = fail "CreateSessionBody"+++data CreateSessionResponse = CreateSessionResponse+ { csrSessionId :: !SessionId+ , csrSessionObjId :: !ObjId+ }++instance ToJSON CreateSessionResponse where+ toJSON x = object+ [ "id" .= csrSessionId x+ , "objId" .= csrSessionObjId x+ ]++++--------------------------------------------------------------------------------+-- LookupSession++data LookupSessionResponse = LookupSessionResponse+ { lsrSessionId :: !SessionId+ , lsrSessionObjId :: !ObjId+ }++instance ToJSON LookupSessionResponse where+ toJSON x = object+ [ "id" .= lsrSessionId x+ , "objId" .= lsrSessionObjId x+ ]+++--------------------------------------------------------------------------------+-- ChangeSecret++data ChangeSecretBody = ChangeSecretBody+ { csbNewSecret :: !Text+ }++instance FromJSON ChangeSecretBody where+ parseJSON (A.Object o) = ChangeSecretBody <$> o .: "secret"+ parseJSON _ = fail "ChangeSecretBody"++++--------------------------------------------------------------------------------+-- UploadBlob++data UploadBlobResponse = UploadBlobResponse+ {+ }++++--------------------------------------------------------------------------------+-- LookupBlob++data LookupBlobResponse = LookupBlobResponse+ {+ }++++--------------------------------------------------------------------------------+-- Signup++data SignupBody = SignupBody+ {+ }++instance FromJSON SignupBody where+ parseJSON = undefined++data SignupResponse = SignupResponse+ {+ }++instance ToJSON SignupResponse where+ toJSON = undefined