servant API type is complete
This commit is contained in:
parent
4133063ab1
commit
1dbb5ec70d
4 changed files with 171 additions and 100 deletions
|
@ -12,14 +12,7 @@
|
|||
module Command.P2PHttp where
|
||||
|
||||
import Command
|
||||
import qualified P2P.Protocol as P2P
|
||||
import Utility.Base64
|
||||
import Utility.MonotonicClock
|
||||
|
||||
import Servant
|
||||
import Servant.API.WebSocket
|
||||
import qualified Data.Text.Encoding as TE
|
||||
import qualified Data.ByteString as B
|
||||
import P2P.Http
|
||||
|
||||
cmd :: Command
|
||||
cmd = command "p2phttp" SectionPlumbing
|
||||
|
@ -28,93 +21,3 @@ cmd = command "p2phttp" SectionPlumbing
|
|||
|
||||
seek :: CmdParams -> CommandSeek
|
||||
seek = error "TODO"
|
||||
|
||||
type API
|
||||
= "git-annex"
|
||||
:> (("key" :> CaptureKey) :<|> ("v3" :> "key" :> CaptureKey))
|
||||
:> CommonParams Optional
|
||||
:> AssociatedFileParam
|
||||
:> OffsetParam
|
||||
:> StreamGet NoFraming OctetStream (SourceIO B.ByteString)
|
||||
:<|> "git-annex" :> "v3" :> "checkpresent"
|
||||
:> KeyParam
|
||||
:> CommonParams Required
|
||||
:> Post '[JSON] CheckPresentResult
|
||||
:<|> "git-annex" :> "v3" :> "lockcontent"
|
||||
:> KeyParam
|
||||
:> CommonParams Required
|
||||
:> WebSocket
|
||||
:<|> "git-annex" :> "v3" :> "remove"
|
||||
:> KeyParam
|
||||
:> CommonParams Required
|
||||
:> Post '[JSON] RemoveResult
|
||||
:<|> "git-annex" :> "v3" :> "remove-before"
|
||||
:> KeyParam
|
||||
:> CommonParams Required
|
||||
:> QueryParam' '[Required] "timestamp" MonotonicTimestamp
|
||||
:> Post '[JSON] RemoveResult
|
||||
:<|> "git-annex" :> "v3" :> "gettimestamp"
|
||||
:> CommonParams Required
|
||||
:> Post '[JSON] GetTimestampResult
|
||||
:<|> "git-annex" :> "v3" :> "put"
|
||||
:> KeyParam
|
||||
:> AssociatedFileParam
|
||||
:> OffsetParam
|
||||
:> Header' '[Required] "X-git-annex-object-size" ObjectSize
|
||||
:> CommonParams Required
|
||||
:> StreamBody NoFraming OctetStream (SourceIO B.ByteString)
|
||||
:> Post '[JSON] PutResult
|
||||
|
||||
type CommonParams req
|
||||
= QueryParam' '[req] "clientuuid" B64UUID
|
||||
:> QueryParam' '[req] "serveruuid" B64UUID
|
||||
:> QueryParams "bypass" B64UUID
|
||||
|
||||
type CaptureKey = Capture "key" B64Key
|
||||
|
||||
type KeyParam = QueryParam' '[Required] "key"
|
||||
|
||||
type AssociatedFileParam = QueryParam "associatedfile" B64FilePath
|
||||
|
||||
type OffsetParam = QueryParam "offset" P2P.Offset
|
||||
|
||||
type GetKey
|
||||
= Capture "key" B64Key
|
||||
:> CommonParams Optional
|
||||
:> AssociatedFileParam
|
||||
:> OffsetParam
|
||||
:> StreamGet NoFraming OctetStream (SourceIO B.ByteString)
|
||||
|
||||
newtype ObjectSize = ObjectSize Integer
|
||||
|
||||
newtype CheckPresentResult = CheckPresentResult Bool
|
||||
|
||||
newtype RemoveResult = RemoveResult Bool
|
||||
|
||||
newtype GetTimestampResult = GetTimestmapResult MonotonicTimestamp
|
||||
|
||||
newtype PutResult = PutResult Bool
|
||||
|
||||
-- Keys, UUIDs, and filenames are base64 encoded since Servant uses
|
||||
-- Text and so needs UTF-8.
|
||||
newtype B64Key = B64Key Key
|
||||
newtype B64UUID = B64UUID UUID
|
||||
newtype B64FilePath = B64FilePath RawFilePath
|
||||
|
||||
instance FromHttpApiData B64Key where
|
||||
parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
|
||||
Nothing -> Left "unable to base64 decode key"
|
||||
Just b -> maybe (Left "key parse error") (Right . B64Key)
|
||||
(deserializeKey' b)
|
||||
|
||||
instance FromHttpApiData B64UUID where
|
||||
parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
|
||||
Nothing -> Left "unable to base64 decode UUID"
|
||||
Just b -> case toUUID b of
|
||||
u@(UUID _) -> Right (B64UUID u)
|
||||
NoUUID -> Left "empty UUID"
|
||||
|
||||
instance FromHttpApiData B64FilePath where
|
||||
parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
|
||||
Nothing -> Left "unable to base64 decode filename"
|
||||
Just b -> Right (B64FilePath b)
|
||||
|
|
167
P2P/Http.hs
Normal file
167
P2P/Http.hs
Normal file
|
@ -0,0 +1,167 @@
|
|||
{- P2P protocol over HTTP
|
||||
-
|
||||
- https://git-annex.branchable.com/design/p2p_protocol_over_http/
|
||||
-
|
||||
- Copyright 2024 Joey Hess <id@joeyh.name>
|
||||
-
|
||||
- Licensed under the GNU AGPL version 3 or higher.
|
||||
-}
|
||||
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE TypeOperators #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module P2P.Http where
|
||||
|
||||
import Annex.Common
|
||||
import qualified P2P.Protocol as P2P
|
||||
import Utility.Base64
|
||||
import Utility.MonotonicClock
|
||||
|
||||
import Servant
|
||||
import Servant.API.WebSocket
|
||||
import qualified Data.Text.Encoding as TE
|
||||
import qualified Data.ByteString as B
|
||||
|
||||
type P2PAPI
|
||||
= "git-annex" :> "key" :> CaptureKey :> GetAPI '[]
|
||||
:<|> "git-annex" :> "v3" :> "key" :> CaptureKey
|
||||
:> GetAPI '[DataLengthHeader]
|
||||
:<|> "git-annex" :> "v2" :> "key" :> CaptureKey
|
||||
:> GetAPI '[DataLengthHeader]
|
||||
:<|> "git-annex" :> "v1" :> "key" :> CaptureKey
|
||||
:> GetAPI '[DataLengthHeader]
|
||||
:<|> "git-annex" :> "v0" :> "key" :> CaptureKey
|
||||
:> GetAPI '[]
|
||||
:<|> "git-annex" :> "v3" :> "checkpresent" :> CheckPresentAPI
|
||||
:<|> "git-annex" :> "v2" :> "checkpresent" :> CheckPresentAPI
|
||||
:<|> "git-annex" :> "v1" :> "checkpresent" :> CheckPresentAPI
|
||||
:<|> "git-annex" :> "v0" :> "checkpresent" :> CheckPresentAPI
|
||||
:<|> "git-annex" :> "v3" :> "lockcontent" :> LockContentAPI
|
||||
:<|> "git-annex" :> "v2" :> "lockcontent" :> LockContentAPI
|
||||
:<|> "git-annex" :> "v1" :> "lockcontent" :> LockContentAPI
|
||||
:<|> "git-annex" :> "v0" :> "lockcontent" :> LockContentAPI
|
||||
:<|> "git-annex" :> "v3" :> "remove" :> RemoveAPI RemoveResultPlus
|
||||
:<|> "git-annex" :> "v2" :> "remove" :> RemoveAPI RemoveResultPlus
|
||||
:<|> "git-annex" :> "v1" :> "remove" :> RemoveAPI RemoveResult
|
||||
:<|> "git-annex" :> "v0" :> "remove" :> RemoveAPI RemoveResult
|
||||
:<|> "git-annex" :> "v3" :> "remove-before" :> RemoveBeforeAPI
|
||||
:<|> "git-annex" :> "v3" :> "gettimestamp" :> GetTimestampAPI
|
||||
:<|> "git-annex" :> "v3" :> "put" :> DataLengthHeader
|
||||
:> PutAPI PutResultPlus
|
||||
:<|> "git-annex" :> "v2" :> "put" :> DataLengthHeader
|
||||
:> PutAPI PutResultPlus
|
||||
:<|> "git-annex" :> "v1" :> "put" :> DataLengthHeader
|
||||
:> PutAPI PutResult
|
||||
:<|> "git-annex" :> "v0" :> "put"
|
||||
:> PutAPI PutResult
|
||||
:<|> "git-annex" :> "v3" :> "putoffset"
|
||||
:> PutOffsetAPI PutOffsetResultPlus
|
||||
:<|> "git-annex" :> "v2" :> "putoffset"
|
||||
:> PutOffsetAPI PutOffsetResultPlus
|
||||
:<|> "git-annex" :> "v1" :> "putoffset"
|
||||
:> PutOffsetAPI PutOffsetResult
|
||||
|
||||
type GetAPI headers
|
||||
= CommonParams Optional
|
||||
:> AssociatedFileParam
|
||||
:> OffsetParam
|
||||
:> StreamGet NoFraming OctetStream
|
||||
(Headers headers (SourceIO B.ByteString))
|
||||
|
||||
type CheckPresentAPI
|
||||
= KeyParam
|
||||
:> CommonParams Required
|
||||
:> Post '[JSON] CheckPresentResult
|
||||
|
||||
type LockContentAPI
|
||||
= KeyParam
|
||||
:> CommonParams Required
|
||||
:> WebSocket
|
||||
|
||||
type RemoveAPI result
|
||||
= KeyParam
|
||||
:> CommonParams Required
|
||||
:> Post '[JSON] result
|
||||
|
||||
type RemoveBeforeAPI
|
||||
= KeyParam
|
||||
:> CommonParams Required
|
||||
:> QueryParam' '[Required] "timestamp" MonotonicTimestamp
|
||||
:> Post '[JSON] RemoveResult
|
||||
|
||||
type GetTimestampAPI
|
||||
= CommonParams Required
|
||||
:> Post '[JSON] GetTimestampResult
|
||||
|
||||
type PutAPI result
|
||||
= KeyParam
|
||||
:> CommonParams Required
|
||||
:> AssociatedFileParam
|
||||
:> OffsetParam
|
||||
:> Header' '[Required] "X-git-annex-data-length" DataLength
|
||||
:> CommonParams Required
|
||||
:> StreamBody NoFraming OctetStream (SourceIO B.ByteString)
|
||||
:> Post '[JSON] result
|
||||
|
||||
type PutOffsetAPI result
|
||||
= KeyParam
|
||||
:> CommonParams Required
|
||||
:> Post '[JSON] result
|
||||
|
||||
type CommonParams req
|
||||
= QueryParam' '[req] "clientuuid" B64UUID
|
||||
:> QueryParam' '[req] "serveruuid" B64UUID
|
||||
:> QueryParams "bypass" B64UUID
|
||||
|
||||
type CaptureKey = Capture "key" B64Key
|
||||
|
||||
type KeyParam = QueryParam' '[Required] "key"
|
||||
|
||||
type AssociatedFileParam = QueryParam "associatedfile" B64FilePath
|
||||
|
||||
type OffsetParam = QueryParam "offset" P2P.Offset
|
||||
|
||||
type DataLengthHeader = Header "X-git-annex-data-length" Integer
|
||||
|
||||
newtype DataLength = DataLength Integer
|
||||
|
||||
newtype CheckPresentResult = CheckPresentResult Bool
|
||||
|
||||
newtype RemoveResult = RemoveResult Bool
|
||||
|
||||
data RemoveResultPlus = RemoveResultPlus Bool [UUID]
|
||||
|
||||
newtype GetTimestampResult = GetTimestmapResult MonotonicTimestamp
|
||||
|
||||
newtype PutResult = PutResult Bool
|
||||
|
||||
data PutResultPlus = PutResultPlus Bool [UUID]
|
||||
|
||||
newtype PutOffsetResult = PutOffsetResult P2P.Offset
|
||||
|
||||
data PutOffsetResultPlus = PutOffsetResultPlus P2P.Offset [UUID]
|
||||
|
||||
-- Keys, UUIDs, and filenames are base64 encoded since Servant uses
|
||||
-- Text and so needs UTF-8.
|
||||
newtype B64Key = B64Key Key
|
||||
newtype B64UUID = B64UUID UUID
|
||||
newtype B64FilePath = B64FilePath RawFilePath
|
||||
|
||||
instance FromHttpApiData B64Key where
|
||||
parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
|
||||
Nothing -> Left "unable to base64 decode key"
|
||||
Just b -> maybe (Left "key parse error") (Right . B64Key)
|
||||
(deserializeKey' b)
|
||||
|
||||
instance FromHttpApiData B64UUID where
|
||||
parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
|
||||
Nothing -> Left "unable to base64 decode UUID"
|
||||
Just b -> case toUUID b of
|
||||
u@(UUID _) -> Right (B64UUID u)
|
||||
NoUUID -> Left "empty UUID"
|
||||
|
||||
instance FromHttpApiData B64FilePath where
|
||||
parseUrlPiece t = case fromB64Maybe (TE.encodeUtf8 t) of
|
||||
Nothing -> Left "unable to base64 decode filename"
|
||||
Just b -> Right (B64FilePath b)
|
|
@ -63,7 +63,7 @@ Any request may also optionally include these parameters:
|
|||
This parameter can be given multiple times to list several cluster
|
||||
gateway UUIDs.
|
||||
|
||||
This parameter is only available for v3 and above.
|
||||
This parameter is only available for v2 and above.
|
||||
|
||||
[Internally, git-annex can use these common parameters, plus the protocol
|
||||
version, to create a P2P session. The P2P session is driven through
|
||||
|
@ -79,7 +79,7 @@ It is not part of the P2P protocol per se, but is provided to let
|
|||
other clients than git-annex easily download the content of keys from the
|
||||
http server.
|
||||
|
||||
This behaves almost the same as `GET /git-annex/v3/key/$key`, although its
|
||||
This behaves almost the same as `GET /git-annex/v0/key/$key`, although its
|
||||
behavior may change in later versions.
|
||||
|
||||
When the key is not present on the server, this returns a 404 Not Found.
|
||||
|
|
|
@ -324,6 +324,7 @@ Executable git-annex
|
|||
CPP-Options: -DWITH_SERVANT
|
||||
Other-Modules:
|
||||
Command.P2PHttp
|
||||
P2P.Http
|
||||
|
||||
if (os(windows))
|
||||
Build-Depends:
|
||||
|
|
Loading…
Reference in a new issue