Skip to content

Commit a3f4deb

Browse files
authored
Merge pull request #170 from unisoncomputing/cp/comment-download
History Comment Sync
2 parents 9f188e4 + f2a0af4 commit a3f4deb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1173
-171
lines changed

.github/workflows/ci.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,10 @@ jobs:
330330
331331
# Install ucm
332332
mkdir ucm
333-
curl -L https://github.com/unisonweb/unison/releases/download/release%2F1.0.0/ucm-linux-x64.tar.gz | tar -xz -C ucm
333+
334+
# Use latest trunk build to get comment upload/download support for now.
335+
# Old: https://github.com/unisonweb/unison/releases/download/release%2F1.0.0/ucm-linux-x64.tar.gz
336+
curl -L https://github.com/unisonweb/unison/releases/download/trunk-build/ucm-linux-x64.tar.gz | tar -xz -C ucm
334337
export PATH=$PWD/ucm:$PATH
335338
336339
# Start share and it's dependencies in the background

share-api/package.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ dependencies:
154154
- wai-extra
155155
- wai-middleware-prometheus
156156
- warp
157+
- websockets
157158
- witch
158159
- witherable
159160
- x509

share-api/share-api.cabal

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ library
130130
Share.Utils.Servant.Client
131131
Share.Utils.Servant.PathInfo
132132
Share.Utils.Servant.RawRequest
133+
Share.Utils.Servant.Streaming
133134
Share.Utils.Tags
134135
Share.Utils.Unison
135136
Share.Web.Admin.API
@@ -194,6 +195,9 @@ library
194195
Share.Web.Support.Impl
195196
Share.Web.Support.Types
196197
Share.Web.Types
198+
Share.Web.UCM.HistoryComments.API
199+
Share.Web.UCM.HistoryComments.Impl
200+
Share.Web.UCM.HistoryComments.Queries
197201
Share.Web.UCM.Projects.Impl
198202
Share.Web.UCM.Sync.HashJWT
199203
Share.Web.UCM.Sync.Impl
@@ -356,6 +360,7 @@ library
356360
, wai-extra
357361
, wai-middleware-prometheus
358362
, warp
363+
, websockets
359364
, witch
360365
, witherable
361366
, x509
@@ -513,6 +518,7 @@ executable share-api
513518
, wai-extra
514519
, wai-middleware-prometheus
515520
, warp
521+
, websockets
516522
, witch
517523
, witherable
518524
, x509

share-api/src/Share/Postgres.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ cachedFor = cachedForOf traversed
685685
-- ) SELECT * FROM something JOIN users on something.user_id = users.id
686686
-- |]
687687
-- @@
688-
whenNonEmpty :: forall m f a x. (Monad m, Foldable f, Monoid a) => f x -> m a -> m a
688+
whenNonEmpty :: forall m f a x. (Foldable f, Monoid a, Applicative m) => f x -> m a -> m a
689689
whenNonEmpty f m = if null f then pure mempty else m
690690

691691
timeTransaction :: (QueryM m) => String -> m a -> m a

share-api/src/Share/Postgres/IDs.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ module Share.Postgres.IDs
2323
NamespaceTermMappingId (..),
2424
NamespaceTypeMappingId (..),
2525
ComponentSummaryDigest (..),
26+
PersonalKeyId (..),
2627

2728
-- * Conversions
2829
hash32AsComponentHash_,
@@ -104,6 +105,10 @@ newtype ComponentSummaryDigest = ComponentSummaryDigest {unComponentSummaryDiges
104105
deriving stock (Show, Eq, Ord)
105106
deriving (PG.EncodeValue, PG.DecodeValue) via ByteString
106107

108+
newtype PersonalKeyId = PersonalKeyId {unPersonalKeyId :: Int32}
109+
deriving stock (Eq, Ord, Show)
110+
deriving (PG.DecodeValue, PG.EncodeValue) via Int32
111+
107112
toHash32 :: (Coercible h Hash) => h -> Hash32
108113
toHash32 = Hash32.fromHash . coerce
109114

share-api/src/Share/Postgres/Orphans.hs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ import U.Codebase.TermEdit qualified as TermEdit
3333
import U.Util.Base32Hex qualified as Base32Hex
3434
import Unison.Hash (Hash)
3535
import Unison.Hash qualified as Hash
36-
import Unison.Hash32 (Hash32)
36+
import Unison.Hash32 (Hash32 (..))
3737
import Unison.Hash32 qualified as Hash32
3838
import Unison.Name (Name)
3939
import Unison.NameSegment.Internal (NameSegment (..))
40+
import Unison.Server.HistoryComments.Types
4041
import Unison.SyncV2.Types (CBORBytes (..))
4142
import Unison.Syntax.Name qualified as Name
4243
import UnliftIO (MonadUnliftIO (..))
@@ -103,6 +104,14 @@ deriving via Hash instance FromHttpApiData ComponentHash
103104

104105
deriving via Hash instance ToHttpApiData ComponentHash
105106

107+
deriving via Hash32 instance Hasql.DecodeValue HistoryCommentHash32
108+
109+
deriving via Hash32 instance Hasql.EncodeValue HistoryCommentHash32
110+
111+
deriving via Hash32 instance Hasql.DecodeValue HistoryCommentRevisionHash32
112+
113+
deriving via Hash32 instance Hasql.EncodeValue HistoryCommentRevisionHash32
114+
106115
deriving via Text instance Hasql.DecodeValue NameSegment
107116

108117
deriving via Text instance Hasql.EncodeValue NameSegment

share-api/src/Share/Prelude/Orphans.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
module Share.Prelude.Orphans () where
77

88
import Control.Comonad.Cofree (Cofree (..))
9+
import Control.Monad.Except
910
import Control.Monad.Trans (lift)
1011
import Control.Monad.Trans.Maybe (MaybeT)
1112
import Data.Align (Semialign (..))
@@ -47,3 +48,6 @@ instance From ShortHash Text where
4748

4849
instance (MonadTracer m) => MonadTracer (MaybeT m) where
4950
getTracer = lift getTracer
51+
52+
instance (MonadTracer m) => MonadTracer (ExceptT e m) where
53+
getTracer = lift getTracer

share-api/src/Share/Utils/Logging.hs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import Data.Text qualified as Text
4545
import Data.Text.Encoding qualified as Text
4646
import Data.Text.IO qualified as Text
4747
import GHC.Stack (CallStack, callStack, prettyCallStack)
48+
import Network.WebSockets qualified as WS
4849
import Servant.Client qualified as Servant
4950
import Share.Env.Types qualified as Env
5051
import Share.OAuth.Errors (OAuth2Error)
@@ -56,6 +57,8 @@ import Share.Utils.Logging.Types as X
5657
import Share.Utils.Tags (MonadTags)
5758
import System.Log.FastLogger qualified as FL
5859
import Unison.Server.Backend qualified as Backend
60+
import Unison.Server.HistoryComments.Types (DownloadCommentsResponse (..), UploadCommentsResponse (..))
61+
import Unison.Server.Types (BranchRef (..))
5962
import Unison.Sync.Types qualified as Sync
6063
import Unison.Util.Monoid (intercalateMap)
6164
import Unison.Util.Monoid qualified as Monoid
@@ -267,3 +270,30 @@ instance Loggable Sync.UploadEntitiesError where
267270
Sync.UploadEntitiesError'UserNotFound userHandle ->
268271
textLog ("User not found: " <> userHandle)
269272
& withSeverity UserFault
273+
274+
instance Loggable UploadCommentsResponse where
275+
toLog = \case
276+
UploadCommentsProjectBranchNotFound (BranchRef branchRef) ->
277+
textLog ("Project branch not found: " <> branchRef)
278+
& withSeverity UserFault
279+
UploadCommentsNotAuthorized (BranchRef branchRef) ->
280+
textLog ("Not authorized to upload comments to branch: " <> branchRef)
281+
& withSeverity UserFault
282+
UploadCommentsGenericFailure errMsg ->
283+
textLog ("Upload comments generic failure: " <> errMsg)
284+
& withSeverity Error
285+
286+
instance Loggable WS.ConnectionException where
287+
toLog = withSeverity Error . showLog
288+
289+
instance Loggable DownloadCommentsResponse where
290+
toLog = \case
291+
DownloadCommentsProjectBranchNotFound (BranchRef branchRef) ->
292+
textLog ("Project branch not found: " <> branchRef)
293+
& withSeverity UserFault
294+
DownloadCommentsNotAuthorized (BranchRef branchRef) ->
295+
textLog ("Not authorized to download comments from branch: " <> branchRef)
296+
& withSeverity UserFault
297+
DownloadCommentsGenericFailure errMsg ->
298+
textLog ("Download comments generic failure: " <> errMsg)
299+
& withSeverity Error
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
module Share.Utils.Servant.Streaming
2+
( toConduit,
3+
cborStreamToConduit,
4+
fromConduit,
5+
sourceIOWithAsync,
6+
queueToCBORStream,
7+
queueToSourceIO,
8+
)
9+
where
10+
11+
-- Orphan instances for SourceIO
12+
13+
import Codec.Serialise qualified as CBOR
14+
import Conduit
15+
import Control.Concurrent.STM.TBMQueue qualified as STM
16+
import Control.Monad.Except
17+
import Data.ByteString.Builder qualified as Builder
18+
import Ki.Unlifted qualified as Ki
19+
import Servant
20+
import Servant.Conduit (conduitToSourceIO)
21+
import Servant.Types.SourceT
22+
import Share.Prelude
23+
import Unison.Util.Servant.CBOR
24+
import UnliftIO.STM qualified as STM
25+
26+
-- | Run the provided IO action in the background while streaming results.
27+
--
28+
-- Servant doesn't provide any easier way to do bracketing like this, all the IO must be
29+
-- inside the SourceIO somehow.
30+
sourceIOWithAsync :: IO a -> SourceIO r -> SourceIO r
31+
sourceIOWithAsync action (SourceT k) =
32+
SourceT \k' ->
33+
Ki.scoped \scope -> do
34+
_ <- Ki.fork scope action
35+
k k'
36+
37+
toConduit :: (MonadIO m, MonadIO n) => SourceIO o -> m (ConduitT void o n ())
38+
toConduit sourceIO = fmap (transPipe liftIO) . liftIO $ fromSourceIO $ sourceIO
39+
40+
cborStreamToConduit :: (MonadIO m, MonadIO n, CBOR.Serialise o) => SourceIO (CBORStream o) -> m (ConduitT void o (ExceptT CBORStreamError n) ())
41+
cborStreamToConduit sourceIO = toConduit sourceIO <&> \stream -> (stream .| unpackCBORBytesStream)
42+
43+
fromConduit :: ConduitT void o IO () -> SourceIO o
44+
fromConduit = conduitToSourceIO
45+
46+
queueToCBORStream :: forall a f. (CBOR.Serialise a, Foldable f) => STM.TBMQueue (f a) -> ConduitT () (CBORStream a) IO ()
47+
queueToCBORStream q = do
48+
let loop :: ConduitT () (CBORStream a) IO ()
49+
loop = do
50+
liftIO (STM.atomically (STM.readTBMQueue q)) >>= \case
51+
-- The queue is closed.
52+
Nothing -> do
53+
pure ()
54+
Just batches -> do
55+
batches
56+
& foldMap (CBOR.serialiseIncremental)
57+
& (CBORStream . Builder.toLazyByteString)
58+
& Conduit.yield
59+
loop
60+
loop
61+
62+
queueToSourceIO :: forall a f. (CBOR.Serialise a, Foldable f) => STM.TBMQueue (f a) -> SourceIO (CBORStream a)
63+
queueToSourceIO q = fromConduit (queueToCBORStream q)

share-api/src/Share/Web/API.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Share.Web.Share.Webhooks.API qualified as Webhooks
1818
import Share.Web.Support.API qualified as Support
1919
import Share.Web.Types
2020
import Share.Web.UCM.SyncV2.API qualified as SyncV2
21+
import Unison.Server.HistoryComments.API qualified as Unison.HistoryComments
2122
import Unison.Share.API.Projects qualified as UCMProjects
2223
import Unison.Sync.API qualified as Unison.Sync
2324

@@ -53,6 +54,7 @@ type API =
5354
-- This path is deprecated, but is still in use by existing clients.
5455
:<|> ("sync" :> MaybeAuthenticatedSession :> Unison.Sync.API)
5556
:<|> ("ucm" :> "v1" :> "sync" :> MaybeAuthenticatedSession :> Unison.Sync.API)
57+
:<|> ("ucm" :> "v1" :> "history-comments" :> MaybeAuthenticatedUserId :> Unison.HistoryComments.API)
5658
:<|> ("ucm" :> "v1" :> "projects" :> MaybeAuthenticatedSession :> UCMProjects.ProjectsAPI)
5759
:<|> ("ucm" :> "v2" :> "sync" :> MaybeAuthenticatedUserId :> SyncV2.API)
5860
:<|> ("admin" :> Admin.API)

0 commit comments

Comments
 (0)